WP Security Audit Log - Version 4.4.0

Version Description

(2022-02-08) =

Release notes: New Reports engine with more criteria, reports management & more

  • New activity log event IDs

    • ID 6059: Changed the site's title.
    • ID 4021: Changed the website URL in the user profile.
    • ID 4013: User has been activated on a multisite network.
  • New features & functionality

    • Hooks to allow users to change the columns in reports or ad value from non-default columns. Refer to the list of hooks in WP Activity Log for more information.
    • New UI for "Enable/Disable event IDs" with search and filtering functionality.
    • New filter that allows user to add metadata to user information popup. Refer to the List of hooks in WP Activity Log for more information.
    • The new Activity Log for TablePress extension.
  • Improvements

    • Changed the database schema for improved storing of data, and faster writing and reading. After the upgrade the plugin will launch the upgrade process which might take some time to complete, depending on the amount of data in the activity log.
    • Improved the coverage of changes done to a website via REST API.
    • Removed obsolete code used for advertorial events in the activity log viewer.
    • All plugin settings now have the wsal_ prefix automatically added to them.
    • Rewritten some of the settings help text in the plugin to better explain the settings.
    • Removed obsolete settings & code of the old file integrity scanner (now part of Website File Changes Monitor plugin).
    • Removed obsolete reference to the old file changes scanner in the daily summary email.
    • Made a number of JS strings available for translation.
    • Removed a number of plugin settings from autoload for improved performance.
    • Improved the plugin's metadata and added the licensing information.
    • Long URL strings in activity log events are now automatically truncated. Full URL can be seen with just a click.
    • Removed forced database table collation: plugin now uses the default WordPress table collation.
    • Updated the "Help & Contact Us" page; improved text and added more relevant information.
    • Improved several UI sections in the Third Party Connections module.
    • Improved the check for writing activity log to external database; now it is less restrictive and faster.
  • Security fix

    • Upgraded the Freemius SDK to version 2.4.3.
  • Bug fixes

    • Fixed: Database error when trying to log in with a non-existing user and a login notification is enabled.
    • Fixed: In some edge cases the plugin was creating an empty "external database" connection string.
    • Fixed a number of typos in the text of activity log events.
    • Fixed: Auto complete in the Delete activity log data section was not returning the correct list of objects.
    • Fixed: Wrong object reported for event ID 5029.
    • Fixed: Event ID 4000 not reported when front-end sensor is disabled.
    • Fixed: "Unknown connection type" reported back setting up a third party connection on specific versions of WordPress.
    • Fixed: Event ID 6320 (added / removed connection) reported instead of event ID 6321 (modified connection).
    • Fixed: Function that was running on "add_filter" instead of "add_action" - Support ticket.
    • Fixed: PHP warning about OPCacheUtils.php in specific setups.
    • Fixed: Edge case in which other plugins couldn't be installed or updated when WP Activity Log was activated.

Refer to the complete plugin changelog for more detailed information about what was new, improved and fixed in previous versions of the WP Activity Log plugin.

Download this release

Release Info

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

Code changes from version 4.3.6 to 4.4.0

Files changed (108) hide show
  1. classes/AbstractSensor.php +3 -2
  2. classes/Adapters/ActiveRecordInterface.php +33 -20
  3. classes/Adapters/MetaInterface.php +3 -5
  4. classes/Adapters/MySQL/ActiveRecordAdapter.php +551 -602
  5. classes/Adapters/MySQL/MetaAdapter.php +18 -20
  6. classes/Adapters/MySQL/OccurrenceAdapter.php +225 -152
  7. classes/Adapters/MySQL/QueryAdapter.php +15 -16
  8. classes/Adapters/MySQL/TmpUserAdapter.php +7 -12
  9. classes/Adapters/OccurrenceInterface.php +51 -10
  10. classes/Adapters/QueryInterface.php +1 -0
  11. classes/Alert.php +19 -14
  12. classes/AlertFormatter.php +12 -0
  13. classes/AlertManager.php +127 -458
  14. classes/AuditLogGridView.php +2 -3
  15. classes/AuditLogListView.php +6 -16
  16. classes/Connector/ConnectorFactory.php +66 -17
  17. classes/Connector/ConnectorInterface.php +13 -1
  18. classes/Connector/MySQLDB.php +44 -19
  19. classes/Helpers/Options.php +16 -29
  20. classes/Loggers/Database.php +2 -61
  21. classes/MainWpApi.php +483 -0
  22. classes/Models/ActiveRecord.php +61 -51
  23. classes/Models/Meta.php +2 -2
  24. classes/Models/Occurrence.php +206 -59
  25. classes/Models/Query.php +4 -3
  26. classes/Multisite/NetworkWide/AbstractTracker.php +1 -1
  27. classes/Multisite/NetworkWide/TrackerInterface.php +2 -2
  28. classes/ReportArgs.php +184 -76
  29. classes/SensorManager.php +15 -12
  30. classes/Sensors/Content.php +108 -89
  31. classes/Sensors/FrontendRegister.php +0 -62
  32. classes/Sensors/LogInOut.php +10 -13
  33. classes/Sensors/Multisite.php +13 -9
  34. classes/Sensors/MultisiteSignUp.php +95 -0
  35. classes/Sensors/Public.php +0 -64
  36. classes/Sensors/Register.php +82 -0
  37. classes/Sensors/System.php +18 -0
  38. classes/Sensors/UserProfile.php +107 -35
  39. classes/Settings.php +31 -27
  40. classes/ThirdPartyExtensions/AbstractExtension.php +8 -0
  41. classes/ThirdPartyExtensions/BBPressExtension.php +8 -4
  42. classes/ThirdPartyExtensions/GravityFormsExtension.php +6 -2
  43. classes/ThirdPartyExtensions/TablePressExtension.php +9 -5
  44. classes/ThirdPartyExtensions/WFCMExtension.php +117 -0
  45. classes/ThirdPartyExtensions/WPFormsExtension.php +6 -2
  46. classes/ThirdPartyExtensions/WooCommerceExtension.php +6 -2
  47. classes/ThirdPartyExtensions/YoastSeoExtension.php +6 -2
  48. classes/Upgrade/MetadataMigration.php +195 -0
  49. classes/Upgrade/Upgrade_43000_to_44400.php +174 -0
  50. classes/Utilities/FileSystemUtils.php +47 -0
  51. classes/Utilities/PluginInstallAndActivate.php +7 -6
  52. classes/Utilities/PluginInstallerAction.php +18 -7
  53. classes/Utilities/RequestUtils.php +11 -0
  54. classes/Utilities/UserUtils.php +28 -7
  55. classes/ViewManager.php +1 -49
  56. classes/Views/AuditLog.php +32 -19
  57. classes/Views/EmailNotifications.php +2 -2
  58. classes/Views/Help.php +14 -8
  59. classes/Views/Reports.php +2 -2
  60. classes/Views/Search.php +2 -2
  61. classes/Views/Settings.php +44 -25
  62. classes/Views/SetupWizard.php +7 -6
  63. classes/Views/ToggleAlerts.php +460 -370
  64. classes/Views/addons/html-view.php +2 -0
  65. css/admin-notices.css +12 -1
  66. defaults.php +58 -3
  67. img/addons/wfcm.png +0 -0
  68. img/help/c4wp.jpg +0 -0
  69. img/help/password-policy-manager.jpg +0 -0
  70. img/help/wp-2fa-img.jpg +0 -0
  71. js/auditlog.js +7 -0
  72. js/common.js +51 -35
  73. languages/index.php +4 -0
  74. languages/wp-security-audit-log.pot +725 -689
  75. license.txt +674 -0
  76. readme.txt +48 -7
  77. third-party/freemius/wordpress-sdk/includes/class-freemius.php +25 -2
  78. third-party/freemius/wordpress-sdk/includes/managers/class-fs-admin-notice-manager.php +7 -2
  79. third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/ArgumentNotExistException.php +5 -1
  80. third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/EmptyArgumentException.php +5 -1
  81. third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/Exception.php +5 -1
  82. third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/InvalidArgumentException.php +5 -1
  83. third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/OAuthException.php +5 -1
  84. third-party/freemius/wordpress-sdk/includes/sdk/FreemiusBase.php +4 -0
  85. third-party/freemius/wordpress-sdk/includes/sdk/FreemiusWordPress.php +4 -1
  86. third-party/freemius/wordpress-sdk/require.php +5 -1
  87. third-party/freemius/wordpress-sdk/start.php +1 -1
  88. third-party/freemius/wordpress-sdk/templates/account/partials/addon.php +6 -1
  89. third-party/freemius/wordpress-sdk/templates/ajax-loader.php +6 -1
  90. third-party/freemius/wordpress-sdk/templates/debug.php +8 -2
  91. third-party/freemius/wordpress-sdk/templates/firewall-issues-js.php +10 -6
  92. third-party/freemius/wordpress-sdk/templates/partials/network-activation.php +6 -1
  93. third-party/freemius/wordpress-sdk/templates/sticky-admin-notice-js.php +4 -2
  94. third-party/vendor/autoload.php +7 -0
  95. third-party/vendor/composer/ClassLoader.php +445 -0
  96. third-party/vendor/composer/LICENSE +21 -0
  97. third-party/vendor/composer/autoload_classmap.php +12 -0
  98. third-party/vendor/composer/autoload_namespaces.php +9 -0
  99. third-party/vendor/composer/autoload_psr4.php +9 -0
  100. third-party/vendor/composer/autoload_real.php +46 -0
  101. third-party/vendor/composer/autoload_static.php +21 -0
  102. third-party/vendor/deliciousbrains/wp-background-processing/classes/wp-async-request.php +157 -0
  103. third-party/vendor/deliciousbrains/wp-background-processing/classes/wp-background-process.php +440 -0
  104. third-party/vendor/deliciousbrains/wp-background-processing/wp-background-processing.php +25 -0
  105. third-party/vendor/mirazmac/php-requirements-checker/src/Checker.php +780 -0
  106. third-party/vendor/mirazmac/php-requirements-checker/usage/usage.php +23 -0
  107. third-party/vendor/scoper-autoload.php +7 -0
  108. wp-security-audit-log.php +141 -213
classes/AbstractSensor.php CHANGED
@@ -43,7 +43,7 @@ abstract class WSAL_AbstractSensor {
43
  * @return boolean
44
  */
45
  protected function IsMultisite() {
46
- return function_exists( 'is_multisite' ) && is_multisite();
47
  }
48
 
49
  /**
@@ -60,7 +60,8 @@ abstract class WSAL_AbstractSensor {
60
  */
61
  protected function Log( $type, $message, $args ) {
62
  $this->plugin->alerts->Trigger(
63
- $type, array(
 
64
  'Message' => $message,
65
  'Context' => $args,
66
  'Trace' => debug_backtrace(),
43
  * @return boolean
44
  */
45
  protected function IsMultisite() {
46
+ return $this->plugin->IsMultisite();
47
  }
48
 
49
  /**
60
  */
61
  protected function Log( $type, $message, $args ) {
62
  $this->plugin->alerts->Trigger(
63
+ $type,
64
+ array(
65
  'Message' => $message,
66
  'Context' => $args,
67
  'Trace' => debug_backtrace(),
classes/Adapters/ActiveRecordInterface.php CHANGED
@@ -20,17 +20,20 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  interface WSAL_Adapters_ActiveRecordInterface {
21
 
22
  /**
23
- * Is installed?
 
 
 
24
  */
25
  public function IsInstalled();
26
 
27
  /**
28
- * Install.
29
  */
30
  public function Install();
31
 
32
  /**
33
- * Uninstall.
34
  */
35
  public function Uninstall();
36
 
@@ -43,49 +46,59 @@ interface WSAL_Adapters_ActiveRecordInterface {
43
  public function Load( $cond = '%d', $args = array( 1 ) );
44
 
45
  /**
46
- * Save.
47
  *
48
- * @param object $activeRecord - Active Record object.
 
49
  */
50
  public function Save( $activeRecord );
51
 
52
  /**
53
- * Delete.
54
  *
55
- * @param object $activeRecord - Active Record object.
 
56
  */
57
  public function Delete( $activeRecord );
58
 
59
  /**
60
- * Load with Multiple Conditions.
61
  *
62
- * @param string $cond - Query Condition.
63
- * @param array $args - Query arguments.
 
 
64
  */
65
  public function LoadMulti( $cond, $args = array() );
66
 
67
  /**
68
- * Load and call foreach.
 
69
  *
70
- * @param string $callback - Callback.
71
- * @param string $cond - Query Condition.
72
- * @param array $args - Query arguments.
73
  */
74
  public function LoadAndCallForEach( $callback, $cond = '%d', $args = array( 1 ) );
75
 
76
  /**
77
- * Count.
 
78
  *
79
- * @param string $cond - Query Condition.
80
- * @param array $args - Query arguments.
 
81
  */
82
  public function Count( $cond = '%d', $args = array( 1 ) );
83
 
84
  /**
85
- * Multiple Query.
 
 
 
86
  *
87
- * @param array $query - Query Condition.
88
- * @param array $args - Query arguments.
89
  */
90
  public function LoadMultiQuery( $query, $args = array() );
91
 
20
  interface WSAL_Adapters_ActiveRecordInterface {
21
 
22
  /**
23
+ * Returns whether table structure is installed or not.
24
+ *
25
+ * @deprecated
26
+ * @return boolean
27
  */
28
  public function IsInstalled();
29
 
30
  /**
31
+ * Install this ActiveRecord structure into DB.
32
  */
33
  public function Install();
34
 
35
  /**
36
+ * Remove this ActiveRecord structure from DB.
37
  */
38
  public function Uninstall();
39
 
46
  public function Load( $cond = '%d', $args = array( 1 ) );
47
 
48
  /**
49
+ * Save an active record into DB.
50
  *
51
+ * @param object $active_record - ActiveRecord object.
52
+ * @return integer|boolean - Either the number of modified/inserted rows or false on failure.
53
  */
54
  public function Save( $activeRecord );
55
 
56
  /**
57
+ * Delete DB record.
58
  *
59
+ * @param object $active_record - ActiveRecord object.
60
+ * @return int|boolean - Either the amount of deleted rows or False on error.
61
  */
62
  public function Delete( $activeRecord );
63
 
64
  /**
65
+ * Load multiple records from DB.
66
  *
67
+ * @param string $cond (Optional) Load condition (eg: 'some_id = %d' ).
68
+ * @param array $args (Optional) Load condition arguments (rg: array(45) ).
69
+ *
70
+ * @return self[] List of loaded records.
71
  */
72
  public function LoadMulti( $cond, $args = array() );
73
 
74
  /**
75
+ * Load multiple records from DB and call a callback for each record.
76
+ * This function is very memory-efficient, it doesn't load records in bulk.
77
  *
78
+ * @param callable $callback The callback to invoke.
79
+ * @param string $cond (Optional) Load condition.
80
+ * @param array $args (Optional) Load condition arguments.
81
  */
82
  public function LoadAndCallForEach( $callback, $cond = '%d', $args = array( 1 ) );
83
 
84
  /**
85
+ * Count records in the DB matching a condition.
86
+ * If no parameters are given, this counts the number of records in the DB table.
87
  *
88
+ * @param string $cond (Optional) Query condition.
89
+ * @param array $args (Optional) Condition arguments.
90
+ * @return int Number of matching records.
91
  */
92
  public function Count( $cond = '%d', $args = array( 1 ) );
93
 
94
  /**
95
+ * Similar to LoadMulti but allows the use of a full SQL query.
96
+ *
97
+ * @param string $query Full SQL query.
98
+ * @param array $args (Optional) Query arguments.
99
  *
100
+ * @return array List of loaded records.
101
+ * @throws Exception
102
  */
103
  public function LoadMultiQuery( $query, $args = array() );
104
 
classes/Adapters/MetaInterface.php CHANGED
@@ -20,16 +20,14 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  interface WSAL_Adapters_MetaInterface {
21
 
22
  /**
23
- * Create a meta object
24
  *
25
- * @param array $occurrence_ids - Array of meta data.
26
- *
27
- * @return int ID of the new meta data
28
  */
29
  public function DeleteByOccurrenceIds( $occurrence_ids );
30
 
31
  /**
32
- * Load by name and occurrence id.
33
  *
34
  * @param string $meta_name - Meta name.
35
  * @param int $occurrence_id - Occurrence ID.
20
  interface WSAL_Adapters_MetaInterface {
21
 
22
  /**
23
+ * Deletes meta data belonging to given occurrences.
24
  *
25
+ * @param int[] $occurrence_ids - Array of occurrence IDs.
 
 
26
  */
27
  public function DeleteByOccurrenceIds( $occurrence_ids );
28
 
29
  /**
30
+ * Load meta by name and occurrence id.
31
  *
32
  * @param string $meta_name - Meta name.
33
  * @param int $occurrence_id - Occurrence ID.
classes/Adapters/MySQL/ActiveRecordAdapter.php CHANGED
@@ -17,6 +17,7 @@ if ( ! defined( 'ABSPATH' ) ) {
17
  *
18
  * MySQL generic table used for Save, Read, Create or Delete
19
  * elements in the Database.
 
20
  * There are also the functions used in the Report Add-On to get the reports.
21
  *
22
  * @package wsal
@@ -44,32 +45,79 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
44
  */
45
  protected $_idkey = '';
46
 
 
 
 
 
 
 
 
47
  /**
48
  * Method: Constructor.
49
  *
50
- * @param object $conn - DB connection object.
51
  */
52
- public function __construct( $conn ) {
53
- $this->connection = $conn;
54
  }
55
 
56
  /**
57
- * Method: Get connection.
58
  *
59
- * @return object DB connection object.
 
 
 
 
60
  */
61
- public function get_connection() {
62
- return $this->connection;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
 
65
  /**
66
- * Returns table name.
67
  *
68
- * @return string
69
  */
70
- public function GetTable() {
71
- $_wpdb = $this->connection;
72
- return $_wpdb->base_prefix . $this->_table;
73
  }
74
 
75
  /**
@@ -79,16 +127,51 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
79
  */
80
  public function GetWPTable() {
81
  global $wpdb;
 
82
  return $wpdb->base_prefix . $this->_table;
83
  }
84
 
85
  /**
86
- * SQL table options (constraints, foreign keys, indexes etc).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  *
88
  * @return string
89
  */
90
- protected function GetTableOptions() {
91
- return ' PRIMARY KEY (' . $this->_idkey . ')';
 
 
92
  }
93
 
94
  /**
@@ -99,7 +182,7 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
99
  public function GetColumns() {
100
  $model = $this->GetModel();
101
 
102
- if ( ! isset( $this->_column_cache ) ) {
103
  $this->_column_cache = array();
104
  foreach ( array_keys( get_object_vars( $model ) ) as $col ) {
105
  if ( trim( $col ) && $col[0] != '_' ) {
@@ -107,37 +190,68 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
107
  }
108
  }
109
  }
 
110
  return $this->_column_cache;
111
  }
112
 
113
  /**
114
- * Returns whether table structure is installed or not.
115
- *
116
- * @deprecated
117
- * @return boolean
118
  */
119
- public function IsInstalled() {
120
- $_wpdb = $this->connection;
121
- $sql = "SHOW TABLES LIKE '" . $this->GetTable() . "'";
122
-
123
- // Table transient.
124
- $wsal_table_transient = 'wsal_' . strtolower( $this->GetTable() ) . '_status';
125
- $wsal_db_table_status = get_transient( $wsal_table_transient );
126
 
127
- // If transient does not exist, then run SQL query.
128
- if ( ! $wsal_db_table_status ) {
129
- $wsal_db_table_status = strtolower( $_wpdb->get_var( $sql ) ) == strtolower( $this->GetTable() );
130
- set_transient( $wsal_table_transient, $wsal_db_table_status, DAY_IN_SECONDS );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
- return $wsal_db_table_status;
 
133
  }
134
 
135
  /**
136
- * Install this ActiveRecord structure into DB.
 
 
137
  */
138
- public function Install() {
139
- $_wpdb = $this->connection;
140
- $_wpdb->query( $this->_GetInstallQuery() );
141
  }
142
 
143
  /**
@@ -149,7 +263,7 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
149
  }
150
 
151
  /**
152
- * Remove this ActiveRecord structure from DB.
153
  */
154
  public function Uninstall() {
155
  $_wpdb = $this->connection;
@@ -170,14 +284,21 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
170
 
171
  // Query table exists.
172
  $table_exists_query = "SHOW TABLES LIKE '" . $this->GetTable() . "'";
 
173
  return $_wpdb->query( $table_exists_query );
174
  }
175
 
176
  /**
177
- * Save an active record into DB.
178
  *
179
- * @param object $active_record - ActiveRecord object.
180
- * @return integer|boolean - Either the number of modified/inserted rows or false on failure.
 
 
 
 
 
 
181
  */
182
  public function Save( $active_record ) {
183
  $_wpdb = $this->connection;
@@ -185,7 +306,8 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
185
  $data = array();
186
  $format = array();
187
 
188
- foreach ( $this->GetColumns() as $index => $key ) {
 
189
  if ( $key == $this->_idkey ) {
190
  $_id_index = $index;
191
  }
@@ -195,14 +317,17 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
195
  if ( is_int( $copy->$key ) ) {
196
  $deffmt = '%d';
197
  }
 
198
  if ( is_float( $copy->$key ) ) {
199
  $deffmt = '%f';
200
  }
 
201
  if ( is_array( $copy->$key ) || is_object( $copy->$key ) ) {
202
  $data[ $key ] = WSAL_Helpers_DataHelper::JsonEncode( $val );
203
  } else {
204
  $data[ $key ] = $val;
205
  }
 
206
  $format[] = $deffmt;
207
  }
208
 
@@ -216,6 +341,7 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
216
  if ( false !== $result && $_wpdb->insert_id ) {
217
  $copy->setId( $_wpdb->insert_id );
218
  }
 
219
  return $result;
220
  }
221
 
@@ -223,13 +349,14 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
223
  * Load record from DB (Single row).
224
  *
225
  * @param string $cond - (Optional) Load condition.
226
- * @param array $args - (Optional) Load condition arguments.
227
  *
228
  * @return array
229
  */
230
  public function Load( $cond = '%d', $args = array( 1 ) ) {
231
  $_wpdb = $this->connection;
232
  $sql = $_wpdb->prepare( 'SELECT * FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
 
233
  return $_wpdb->get_row( $sql, ARRAY_A );
234
  }
235
 
@@ -237,7 +364,7 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
237
  * Load records from DB (Multi rows).
238
  *
239
  * @param string $cond Load condition.
240
- * @param array $args (Optional) Load condition arguments.
241
  *
242
  * @return array
243
  * @throws Exception
@@ -249,20 +376,22 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
249
  foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
250
  $result[] = $this->getModel()->LoadData( $data );
251
  }
 
252
  return $result;
253
  }
254
 
255
  /**
256
- * Delete DB record.
257
- *
258
- * @param object $active_record - ActiveRecord object.
259
- * @return int|boolean - Either the amount of deleted rows or False on error.
260
  */
261
  public function Delete( $active_record ) {
262
- $_wpdb = $this->connection;
 
263
  return $_wpdb->delete(
264
  $this->GetTable(),
265
- $active_record->getId()
 
 
 
266
  );
267
  }
268
 
@@ -270,24 +399,19 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
270
  * Delete records in DB matching a query.
271
  *
272
  * @param string $query Full SQL query.
273
- * @param array $args (Optional) Query arguments.
274
  *
275
  * @return int|bool
276
  */
277
  public function DeleteQuery( $query, $args = array() ) {
278
- $_wpdb = $this->connection;
279
- $sql = count( $args ) ? $_wpdb->prepare( $query, $args ) : $query;
 
280
  return $_wpdb->query( $sql );
281
  }
282
 
283
  /**
284
- * Load multiple records from DB.
285
- *
286
- * @param string $cond (Optional) Load condition (eg: 'some_id = %d' ).
287
- * @param array $args (Optional) Load condition arguments (rg: array(45) ).
288
- *
289
- * @return self[] List of loaded records.
290
- * @throws Exception
291
  */
292
  public function LoadMulti( $cond, $args = array() ) {
293
  $_wpdb = $this->connection;
@@ -298,16 +422,12 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
298
  foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
299
  $result[] = $this->getModel()->LoadData( $data );
300
  }
 
301
  return $result;
302
  }
303
 
304
  /**
305
- * Load multiple records from DB and call a callback for each record.
306
- * This function is very memory-efficient, it doesn't load records in bulk.
307
- *
308
- * @param callable $callback The callback to invoke.
309
- * @param string $cond (Optional) Load condition.
310
- * @param array $args (Optional) Load condition arguments.
311
  */
312
  public function LoadAndCallForEach( $callback, $cond = '%d', $args = array( 1 ) ) {
313
  $_wpdb = $this->connection;
@@ -319,16 +439,12 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
319
  }
320
 
321
  /**
322
- * Count records in the DB matching a condition.
323
- * If no parameters are given, this counts the number of records in the DB table.
324
- *
325
- * @param string $cond (Optional) Query condition.
326
- * @param array $args (Optional) Condition arguments.
327
- * @return int Number of matching records.
328
  */
329
  public function Count( $cond = '%d', $args = array( 1 ) ) {
330
  $_wpdb = $this->connection;
331
  $sql = $_wpdb->prepare( 'SELECT COUNT(*) FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
 
332
  return (int) $_wpdb->get_var( $sql );
333
  }
334
 
@@ -336,23 +452,19 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
336
  * Count records in the DB matching a query.
337
  *
338
  * @param string $query Full SQL query.
339
- * @param array $args (Optional) Query arguments.
 
340
  * @return int Number of matching records.
341
  */
342
  public function CountQuery( $query, $args = array() ) {
343
  $_wpdb = $this->connection;
344
  $sql = count( $args ) ? $_wpdb->prepare( $query, $args ) : $query;
 
345
  return (int) $_wpdb->get_var( $sql );
346
  }
347
 
348
  /**
349
- * Similar to LoadMulti but allows the use of a full SQL query.
350
- *
351
- * @param string $query Full SQL query.
352
- * @param array $args (Optional) Query arguments.
353
- *
354
- * @return array List of loaded records.
355
- * @throws Exception
356
  */
357
  public function LoadMultiQuery( $query, $args = array() ) {
358
  $_wpdb = $this->connection;
@@ -361,629 +473,496 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
361
  foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
362
  $result[] = $this->getModel()->LoadData( $data );
363
  }
 
364
  return $result;
365
  }
366
 
367
  /**
368
- * Table install query.
369
  *
370
- * @param string|false $prefix - (Optional) Table prefix.
371
- * @return string - Must return SQL for creating table.
 
 
 
 
 
372
  */
373
- protected function _GetInstallQuery( $prefix = false ) {
374
- $_wpdb = $this->connection;
375
- $class = get_class( $this );
376
- $copy = new $class( $this->connection );
377
- $table_name = ( $prefix ) ? $this->GetWPTable() : $this->GetTable();
378
- $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
379
 
380
- foreach ( $this->GetColumns() as $key ) {
381
- $sql .= ' ';
382
- switch ( true ) {
383
- case $key == $copy->_idkey:
384
- $sql .= $key . ' BIGINT NOT NULL AUTO_INCREMENT,' . PHP_EOL;
385
- break;
386
- case is_int( $copy->$key ):
387
- $sql .= $key . ' BIGINT NOT NULL,' . PHP_EOL;
388
- break;
389
- case is_float( $copy->$key ):
390
- $sql .= $key . ' DOUBLE NOT NULL,' . PHP_EOL;
391
- break;
392
- case is_string( $copy->$key ):
393
- $maxlength = $key . '_maxlength';
394
- if ( property_exists( $class, $maxlength ) ) {
395
- $sql .= $key . ' VARCHAR(' . (int) $class::$$maxlength . ') NOT NULL,' . PHP_EOL;
396
- } else {
397
- $sql .= $key . ' LONGTEXT NOT NULL,' . PHP_EOL;
398
- }
399
- break;
400
- case is_bool( $copy->$key ):
401
- $sql .= $key . ' BIT NOT NULL,' . PHP_EOL;
402
- break;
403
- case is_array( $copy->$key ):
404
- case is_object( $copy->$key ):
405
- $sql .= $key . ' LONGTEXT NOT NULL,' . PHP_EOL;
406
- break;
407
- default:
408
- }
409
- }
410
 
411
- $sql .= $this->GetTableOptions() . PHP_EOL;
 
412
 
413
- $sql .= ')';
 
 
414
 
415
- if ( ! empty( $_wpdb->charset ) ) {
416
- $sql .= ' DEFAULT CHARACTER SET ' . $_wpdb->charset;
 
 
 
 
 
417
  }
418
 
419
- return $sql;
420
  }
421
 
422
  /**
423
- * Update `option_value` column of the Options table
424
- * of WSAL to LONGTEXT.
425
  *
426
- * @since 3.2.3
427
- */
428
- public function update_value_column() {
429
- global $wpdb;
430
- $sql = 'ALTER TABLE ' . $this->GetTable();
431
- $sql .= ' MODIFY COLUMN option_value LONGTEXT NOT NULL';
432
- $wpdb->query( $sql );
433
- }
434
-
435
- /**
436
- * Must return SQL for removing table (at a minimum, it should be ` 'DROP TABLE ' . $this->_table `).
437
  *
438
  * @return string
439
  */
440
- protected function _GetUninstallQuery() {
441
- return 'DROP TABLE IF EXISTS ' . $this->GetTable();
442
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
- /**
445
- * Get Users user_login.
446
- *
447
- * @param int $_user_id - User ID.
448
- * @return string comma separated users login
449
- */
450
- private function GetUserNames( $_user_id ) {
451
- global $wpdb;
452
 
453
- $user_names = '0';
454
- if ( ! empty( $_user_id ) && 'null' != $_user_id ) {
455
- $sql = 'SELECT user_login FROM ' . $wpdb->users . ' WHERE find_in_set(ID, @userId) > 0';
456
- $wpdb->query( "SET @userId = $_user_id" );
457
- $result = $wpdb->get_results( $sql, ARRAY_A );
458
- $users_array = array();
459
- foreach ( $result as $item ) {
460
- $users_array[] = '"' . $item['user_login'] . '"';
461
- }
462
- $user_names = implode( ', ', $users_array );
463
  }
464
- return $user_names;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  }
466
 
467
  /**
468
- * Function used in WSAL reporting extension.
469
  *
470
  * @param WSAL_ReportArgs $report_args Report arguments.
471
- * @param int $_next_date - (Optional) Created on >.
472
- * @param int $_limit - (Optional) Limit.
473
  *
474
- * @return array Report results
475
  */
476
- public function GetReporting( $report_args, $_next_date = null, $_limit = 0 ) {
477
-
478
- $_wpdb = $this->connection;
479
- $_wpdb->set_charset( $_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci' );
480
-
481
- // Tables.
482
- $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
483
- $table_meta = $meta->GetTable(); // Metadata.
484
- $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
485
- $table_occ = $occurrence->GetTable(); // Occurrences.
486
-
487
- $condition_date = ! empty( $_next_date ) ? ' AND occ.created_on < ' . $_next_date : '';
488
-
489
- $_site_id = 'null';
490
  $sites_negate_expression = '';
491
  if ( $report_args->site__in ) {
492
  $_site_id = $this->formatArrayForQuery( $report_args->site__in );
493
- } else if ( $report_args->site__not_in ) {
494
  $_site_id = $this->formatArrayForQuery( $report_args->site__not_in );
495
  $sites_negate_expression = 'NOT';
496
  }
497
 
498
- $_user_id = 'null';
499
  $users_negate_expression = '';
500
- $users_subselect_operand = 'OR';
501
  if ( $report_args->user__in ) {
502
  $_user_id = $this->formatArrayForQuery( $report_args->user__in );
503
- } else if ( $report_args->user__not_in ) {
504
  $_user_id = $this->formatArrayForQuery( $report_args->user__not_in );
505
  $users_negate_expression = 'NOT';
506
- $users_subselect_operand = 'AND';
507
  }
508
 
509
  $user_names = $this->GetUserNames( $_user_id );
510
 
511
- $_role_name = 'null';
512
  $roles_negate_expression = '';
513
  if ( $report_args->role__in ) {
514
  $_role_name = $this->formatArrayForQueryRegex( $report_args->role__in );
515
- } else if ( $report_args->role__not_in ) {
516
  $_role_name = $this->formatArrayForQueryRegex( $report_args->role__not_in );
517
  $roles_negate_expression = 'NOT';
518
  }
519
 
520
- $_alert_code = 'null';
 
521
  if ( $report_args->code__in ) {
522
  $_alert_code = $this->formatArrayForQuery( $report_args->code__in );
 
 
 
523
  }
524
 
525
- $_post_types = 'null';
 
 
 
 
 
 
 
 
 
 
526
  if ( $report_args->post_type__in ) {
527
  $_post_types = $this->formatArrayForQueryRegex( $report_args->post_type__in );
 
 
 
528
  }
529
 
530
- $_post_statuses = 'null';
 
531
  if ( $report_args->post_status__in ) {
532
  $_post_statuses = $this->formatArrayForQueryRegex( $report_args->post_status__in );
 
 
 
533
  }
534
 
535
- $_objects = 'null';
 
 
 
 
 
 
 
 
 
536
  $objects_negate_expression = '';
537
  if ( $report_args->object__in ) {
538
  $_objects = $this->formatArrayForQuery( $report_args->object__in );
539
- } else if ( $report_args->object__not_in ) {
540
  $_objects = $this->formatArrayForQuery( $report_args->object__not_in );
541
  $objects_negate_expression = 'NOT';
542
  }
543
 
544
- $_event_types = 'null';
545
  $event_types_negate_expression = '';
546
  if ( $report_args->type__in ) {
547
  $_event_types = $this->formatArrayForQuery( $report_args->type__in );
548
- } else if ( $report_args->type__not_in ) {
549
- $_event_types = $this->formatArrayForQuery( $report_args->type__not_in );
550
  $event_types_negate_expression = 'NOT';
551
  }
552
 
553
- $_start_timestamp = 'null';
554
  if ( $report_args->start_date ) {
555
  $start_datetime = DateTime::createFromFormat( 'Y-m-d H:i:s', $report_args->start_date . ' 00:00:00' );
556
  $_start_timestamp = $start_datetime->format( 'U' );
557
  }
558
 
559
- $_end_timestamp = 'null';
560
  if ( $report_args->end_date ) {
561
  $end_datetime = DateTime::createFromFormat( 'Y-m-d H:i:s', $report_args->end_date . ' 23:59:59' );
562
  $_end_timestamp = $end_datetime->format( 'U' );
563
  }
564
 
565
- $users_condition = "(
566
- @userId is NULL
567
- OR (
568
- {$users_negate_expression} EXISTS( SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='CurrentUserID' AND find_in_set( meta.value, @userId ) > 0)
569
- $users_subselect_operand
570
- {$users_negate_expression} EXISTS( SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='Username' AND replace(meta.value, '\"', '' ) IN ( $user_names ) )
571
- )
572
- )";
573
-
574
- if ( 'null' === $_post_types && 'null' === $_post_statuses ) {
575
- $sql = "SELECT DISTINCT
576
- occ.id,
577
- occ.alert_id,
578
- occ.site_id,
579
- occ.created_on,
580
- replace(replace(replace((SELECT t1.value FROM $table_meta AS t1 WHERE t1.name = 'CurrentUserRoles' AND t1.occurrence_id = occ.id LIMIT 1), '[', ''), ']', ''), '\\'', '') AS roles,
581
- (SELECT replace(t2.value, '\"','') FROM $table_meta as t2 WHERE t2.name = 'ClientIP' AND t2.occurrence_id = occ.id LIMIT 1) AS ip,
582
- (SELECT replace(t3.value, '\"', '') FROM $table_meta as t3 WHERE t3.name = 'UserAgent' AND t3.occurrence_id = occ.id LIMIT 1) AS ua,
583
- COALESCE(
584
- (SELECT replace(t4.value, '\"', '') FROM $table_meta as t4 WHERE t4.name = 'Username' AND t4.occurrence_id = occ.id LIMIT 1),
585
- (SELECT replace(t5.value, '\"', '') FROM $table_meta as t5 WHERE t5.name = 'CurrentUserID' AND t5.occurrence_id = occ.id LIMIT 1)
586
- ) as user_id,
587
- (SELECT replace(t6.value, '\"', '') FROM $table_meta as t6 WHERE t6.name = 'Object' AND t6.occurrence_id = occ.id LIMIT 1) AS object,
588
- (SELECT replace(t7.value, '\"', '') FROM $table_meta as t7 WHERE t7.name = 'EventType' AND t7.occurrence_id = occ.id LIMIT 1) AS event_type
589
- FROM $table_occ AS occ
590
- JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
591
- WHERE
592
- (
593
- @siteId is NULL
594
- OR
595
- {$sites_negate_expression} find_in_set( occ.site_id, @siteId ) > 0
596
- )
597
- AND {$users_condition}
598
- AND (
599
- @roleName is NULL
600
- OR (
601
- meta.name = 'CurrentUserRoles'
602
- AND
603
- replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') {$roles_negate_expression} REGEXP @roleName
604
- )
605
- )
606
- AND (
607
- @object is NULL
608
- OR
609
- {$objects_negate_expression} EXISTS( SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='Object' AND find_in_set( meta.value, @object ) > 0 )
610
- )
611
- AND (
612
- @eventType is NULL
613
- OR
614
- {$event_types_negate_expression} EXISTS( SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='EventType' AND find_in_set( meta.value, @eventType ) > 0)
615
- )
616
- AND ( @alertCode is NULL OR find_in_set( occ.alert_id, @alertCode ) > 0)
617
- AND ( @startTimestamp is NULL OR occ.created_on >= @startTimestamp )
618
- AND ( @endTimestamp is NULL OR occ.created_on <= @endTimestamp )
619
- {$condition_date}
620
- ORDER BY
621
- created_on DESC
622
- ";
623
- } else {
624
- $sql = "SELECT
625
- occ.id,
626
- occ.alert_id,
627
- occ.site_id,
628
- occ.created_on,
629
- replace(replace(replace((SELECT t1.value FROM $table_meta AS t1 WHERE t1.name = 'CurrentUserRoles' AND t1.occurrence_id = occ.id LIMIT 1), '[', ''), ']', ''), '\\'', '') AS roles,
630
- (SELECT replace(t2.value, '\"','') FROM $table_meta as t2 WHERE t2.name = 'ClientIP' AND t2.occurrence_id = occ.id LIMIT 1) AS ip,
631
- (SELECT replace(t3.value, '\"', '') FROM $table_meta as t3 WHERE t3.name = 'UserAgent' AND t3.occurrence_id = occ.id LIMIT 1) AS ua,
632
- COALESCE(
633
- (SELECT replace(t4.value, '\"', '') FROM $table_meta as t4 WHERE t4.name = 'Username' AND t4.occurrence_id = occ.id LIMIT 1),
634
- (SELECT replace(t5.value, '\"', '') FROM $table_meta as t5 WHERE t5.name = 'CurrentUserID' AND t5.occurrence_id = occ.id LIMIT 1)
635
- ) as user_id,
636
- (SELECT replace(t6.value, '\"', '') FROM $table_meta as t6 WHERE t6.name = 'Object' AND t6.occurrence_id = occ.id LIMIT 1) AS object,
637
- (SELECT replace(t7.value, '\"', '') FROM $table_meta as t7 WHERE t7.name = 'EventType' AND t7.occurrence_id = occ.id LIMIT 1) AS event_type
638
- FROM
639
- $table_occ as occ
640
- WHERE
641
- (
642
- @siteId is NULL
643
- OR
644
- {$sites_negate_expression} find_in_set(occ.site_id, @siteId) > 0
645
- )
646
- AND {$users_condition}
647
- AND (
648
- @roleName is NULL
649
- OR
650
- {$roles_negate_expression} EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='CurrentUserRoles' AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName)
651
- )
652
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
653
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
654
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
655
- AND (
656
- @postType is NULL
657
- OR
658
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='PostType' AND find_in_set(meta.value, @postType) > 0)
659
- )
660
- AND (
661
- @postStatus is NULL
662
- OR
663
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='PostStatus' AND find_in_set(meta.value, @postStatus) > 0)
664
- )
665
- AND (
666
- @object is NULL
667
- OR
668
- {$objects_negate_expression} EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='Object' AND find_in_set(meta.value, @object) > 0)
669
- )
670
- AND (
671
- @eventType is NULL
672
- OR
673
- {$event_types_negate_expression} EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='EventType' AND find_in_set(meta.value, @eventType) > 0)
674
- )
675
- {$condition_date}
676
- ORDER BY
677
- created_on DESC
678
- ";
679
  }
680
 
681
- $_wpdb->query( "SET @siteId = $_site_id" );
682
- $_wpdb->query( "SET @userId = $_user_id" );
683
- $_wpdb->query( "SET @postType = $_post_types" );
684
- $_wpdb->query( "SET @postStatus = $_post_statuses" );
685
- $_wpdb->query( "SET @object = $_objects" );
686
- $_wpdb->query( "SET @eventType = $_event_types" );
687
- $_wpdb->query( "SET @roleName = $_role_name" );
688
- $_wpdb->query( "SET @alertCode = $_alert_code" );
689
- $_wpdb->query( "SET @startTimestamp = $_start_timestamp" );
690
- $_wpdb->query( "SET @endTimestamp = $_end_timestamp" );
691
-
692
- if ( ! empty( $_limit ) ) {
693
- $sql .= " LIMIT {$_limit}";
694
  }
695
- $results = $_wpdb->get_results( $sql );
696
- if ( ! empty( $results ) ) {
697
- $last_item = end( $results );
698
- $results['lastDate'] = $last_item->created_on;
 
699
  }
700
 
701
- return $results;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
702
  }
703
 
704
  /**
705
- * Function used in WSAL reporting extension.
706
- * Check if criteria are matching in the DB.
707
  *
708
- * @param mixed $criteria - Query conditions.
709
- * @return int count of distinct values
710
  */
711
- public function CheckMatchReportCriteria( $criteria ) {
712
- $_site_id = $criteria['siteId'];
713
- $_user_id = $criteria['userId'];
714
- $_post_types = $criteria['post_types'];
715
- $_post_statuses = $criteria['post_statuses'];
716
- $_role_name = $criteria['roleName'];
717
- $_alert_code = $criteria['alertCode'];
718
- $_start_timestamp = $criteria['startTimestamp'];
719
- $_end_timestamp = $criteria['endTimestamp'];
720
- $_ip_address = $criteria['ipAddress'];
721
-
722
- $_wpdb = $this->connection;
723
- $_wpdb->set_charset( $_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci' );
724
- // Tables.
725
- $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
726
- $table_meta = $meta->GetTable(); // Metadata.
727
- $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
728
- $table_occ = $occurrence->GetTable(); // Occurrences.
729
 
730
- $user_names = $this->GetUserNames( $_user_id );
 
 
 
 
 
 
 
 
731
 
732
- if ( 'null' === $_post_types && 'null' === $_post_statuses ) {
733
- $sql = "SELECT COUNT(DISTINCT occ.id) FROM $table_occ AS occ
734
- JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
735
- WHERE
736
- (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
737
- AND (@userId is NULL OR (
738
- (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
739
- OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
740
- ))
741
- AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
742
- AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
743
- ))
744
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
745
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
746
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
747
- AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
748
- ";
749
- } else {
750
- $sql = "SELECT COUNT(DISTINCT occ.id)
751
- FROM $table_occ AS occ
752
- WHERE
753
- (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
754
- AND (
755
- @userId is NULL
756
- OR (
757
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
758
- OR
759
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='Username' AND replace(meta.value, '\"', '') IN ($user_names))
760
- )
761
- )
762
- AND (
763
- @roleName is NULL
764
- OR
765
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='CurrentUserRoles' AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName)
766
- )
767
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
768
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
769
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
770
- AND (
771
- @ipAddress is NULL
772
- OR
773
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='ClientIP' AND find_in_set(meta.value, @ipAddress) > 0)
774
- )
775
- AND (
776
- @postType is NULL
777
- OR
778
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='PostType' AND find_in_set(meta.value, @postType) > 0)
779
- )
780
- AND (
781
- @postStatus is NULL
782
- OR
783
- EXISTS(SELECT 1 FROM $table_meta as meta WHERE meta.occurrence_id = occ.id AND meta.name='PostStatus' AND find_in_set(meta.value, @postStatus) > 0)
784
- )
785
- ";
786
  }
787
 
788
- $_wpdb->query( "SET @siteId = $_site_id" );
789
- $_wpdb->query( "SET @userId = $_user_id" );
790
- $_wpdb->query( "SET @postType = $_post_types" );
791
- $_wpdb->query( "SET @postStatus = $_post_statuses" );
792
- $_wpdb->query( "SET @roleName = $_role_name" );
793
- $_wpdb->query( "SET @alertCode = $_alert_code" );
794
- $_wpdb->query( "SET @startTimestamp = $_start_timestamp" );
795
- $_wpdb->query( "SET @endTimestamp = $_end_timestamp" );
796
- $_wpdb->query( "SET @ipAddress = $_ip_address" );
797
 
798
- return (int) $_wpdb->get_var( $sql );
 
 
 
 
 
 
 
 
 
 
 
 
799
  }
800
 
801
  /**
802
  * Function used in WSAL reporting extension.
803
- * List of unique IP addresses used by the same user.
804
  *
805
- * @param WSAL_ReportArgs $report_args Report arguments.
806
- * @param int $_limit - (Optional) Limit.
807
  *
808
- * @return array Report results grouped by IP and Username
809
  */
810
- public function GetReportGrouped( $report_args, $_limit = 0 ) {
 
811
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  global $wpdb;
813
  $_wpdb = $this->connection;
814
- $_wpdb->set_charset( $_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci' );
815
 
816
  // Tables.
817
- $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
818
- $table_meta = $meta->GetTable(); // Metadata.
819
- $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
820
- $table_occ = $occurrence->GetTable(); // Occurrences.
821
  // Get temp table `wsal_tmp_users`.
822
- $tmp_users = new WSAL_Adapters_MySQL_TmpUser( $this->connection );
823
  // If the table exist.
824
  if ( $tmp_users->IsInstalled() ) {
825
- $table_users = $tmp_users->GetTable(); // tmp_users.
826
  $this->TempUsers( $table_users );
827
  } else {
828
  $table_users = $wpdb->users;
829
  }
830
 
831
- $_site_id = 'null';
832
- $sites_negate_expression = '';
833
- if ( $report_args->site__in ) {
834
- $_site_id = $this->formatArrayForQuery( $report_args->site__in );
835
- } else if ( $report_args->site__not_in ) {
836
- $_site_id = $this->formatArrayForQuery( $report_args->site__not_in );
837
- $sites_negate_expression = 'NOT';
838
- }
839
-
840
- $_user_id = 'null';
841
- $users_negate_expression = '';
842
- if ( $report_args->user__in ) {
843
- $_user_id = $this->formatArrayForQuery( $report_args->user__in );
844
- } else if ( $report_args->user__not_in ) {
845
- $_user_id = $this->formatArrayForQuery( $report_args->user__not_in );
846
- $users_negate_expression = 'NOT';
847
- }
848
 
849
- $_role_name = 'null';
850
- $roles_negate_expression = '';
851
- if ( $report_args->role__in ) {
852
- $_role_name = $this->formatArrayForQueryRegex( $report_args->role__in );
853
- } else if ( $report_args->role__not_in ) {
854
- $_role_name = $this->formatArrayForQueryRegex( $report_args->role__not_in );
855
- $roles_negate_expression = 'NOT';
856
- }
857
 
858
- $_alert_code = 'null';
859
- if ( $report_args->code__in ) {
860
- $_alert_code = $this->formatArrayForQuery( $report_args->code__in );
861
  }
862
 
863
- $_ip_address = 'null';
864
- $ip_address_negate_expression = '';
865
- if ( $report_args->ip__in ) {
866
- $_ip_address = $this->formatArrayForQuery( $report_args->ip__in );
867
- } else if ( $report_args->ip__not_in ) {
868
- $_ip_address = $this->formatArrayForQuery( $report_args->ip__not_in );
869
- $ip_address_negate_expression = 'NOT';
870
  }
871
 
872
- $_start_timestamp = 'null';
873
- if ( $report_args->start_date ) {
874
- $start_datetime = DateTime::createFromFormat( 'Y-m-d H:i:s', $report_args->start_date . ' 00:00:00' );
875
- $_start_timestamp = $start_datetime->format( 'U' );
876
- }
877
-
878
- $_end_timestamp = 'null';
879
- if ( $report_args->end_date ) {
880
- $end_datetime = DateTime::createFromFormat( 'Y-m-d H:i:s', $report_args->end_date . ' 23:59:59' );
881
- $_end_timestamp = $end_datetime->format( 'U' );
 
 
 
 
 
 
882
  }
883
 
884
- $user_names = $this->GetUserNames( $_user_id );
885
 
886
- $_wpdb->query( "SET @siteId = $_site_id" );
887
- $_wpdb->query( "SET @userId = $_user_id" );
888
- $_wpdb->query( "SET @roleName = $_role_name" );
889
- $_wpdb->query( "SET @alertCode = $_alert_code" );
890
- $_wpdb->query( "SET @startTimestamp = $_start_timestamp" );
891
- $_wpdb->query( "SET @endTimestamp = $_end_timestamp" );
892
- $_wpdb->query( "SET @ipAddress = $_ip_address" );
893
-
894
- $sql = "SELECT DISTINCT *
895
- FROM (SELECT DISTINCT
896
- occ.site_id,
897
- CONVERT((SELECT replace(t1.value, '\"', '') FROM $table_meta as t1 WHERE t1.name = 'Username' AND t1.occurrence_id = occ.id LIMIT 1) using UTF8) AS user_login ,
898
- CONVERT((SELECT replace(t3.value, '\"','') FROM $table_meta as t3 WHERE t3.name = 'ClientIP' AND t3.occurrence_id = occ.id LIMIT 1) using UTF8) AS ip
899
  FROM $table_occ AS occ
900
- JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
901
- WHERE
902
- (
903
- @siteId is NULL
904
- OR
905
- {$sites_negate_expression} find_in_set(occ.site_id, @siteId) > 0
906
- )
907
- AND (
908
- @userId is NULL
909
- OR $users_negate_expression (
910
- ( meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0 )
911
- OR
912
- ( meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names) )
913
- )
914
- )
915
- AND (
916
- @roleName is NULL
917
- OR (
918
- meta.name = 'CurrentUserRoles' AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') {$roles_negate_expression} REGEXP @roleName
919
- )
920
- )
921
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
922
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
923
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
924
- AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND {$ip_address_negate_expression} find_in_set(meta.value, @ipAddress) > 0))
925
- HAVING user_login IS NOT NULL
926
- UNION ALL
927
- SELECT DISTINCT
928
- occ.site_id,
929
- CONVERT((SELECT u.user_login
930
- FROM $table_meta as t2
931
- JOIN $table_users AS u ON u.ID = replace(t2.value, '\"', '')
932
- WHERE t2.name = 'CurrentUserID'
933
- AND t2.occurrence_id = occ.id
934
- GROUP BY u.ID
935
- LIMIT 1) using UTF8) AS user_login,
936
- CONVERT((SELECT replace(t4.value, '\"','') FROM $table_meta as t4 WHERE t4.name = 'ClientIP' AND t4.occurrence_id = occ.id LIMIT 1) using UTF8) AS ip
937
  FROM $table_occ AS occ
938
- JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
939
- WHERE
940
- (@siteId is NULL OR {$sites_negate_expression} find_in_set(occ.site_id, @siteId) > 0)
941
- AND (@userId is NULL OR {$users_negate_expression} (
942
- (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
943
- OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
944
- ))
945
- AND (@roleName is NULL OR {$roles_negate_expression} (meta.name = 'CurrentUserRoles'
946
- AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
947
- ))
948
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
949
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
950
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
951
- AND (@ipAddress is NULL OR {$ip_address_negate_expression} (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
952
- HAVING user_login IS NOT NULL) ip_logins
953
- WHERE user_login NOT IN ('Unregistered user', 'Plugins', 'Plugin')
954
- ORDER BY user_login ASC
955
- ";
956
-
957
- if ( ! empty( $_limit ) ) {
958
- $sql .= " LIMIT {$_limit}";
959
  }
960
 
961
- $grouped_types = array();
962
- $results = $_wpdb->get_results( $sql );
963
- if ( ! empty( $results ) ) {
964
- foreach ( $results as $key => $row ) {
965
- // Get the display_name only for the first row & if the user_login changed from the previous row.
966
- $row->display_name = '';
967
- if ( 0 == $key || ( $key > 1 && $results[ ( $key - 1 ) ]->user_login != $row->user_login ) ) {
968
- $sql = "SELECT t5.display_name FROM $wpdb->users AS t5 WHERE t5.user_login = \"$row->user_login\"";
969
- $display_name = $wpdb->get_var( $sql );
970
- $row->display_name = $display_name;
971
- }
972
 
973
- if ( ! isset( $grouped_types[ $row->user_login ] ) ) {
974
- $grouped_types[ $row->user_login ] = array(
975
- 'site_id' => $row->site_id,
976
- 'user_login' => $row->user_login,
977
- 'display_name' => $row->display_name,
978
- 'ips' => array(),
979
- );
980
- }
981
 
982
- $grouped_types[ $row->user_login ]['ips'][] = $row->ip;
983
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
984
  }
985
 
986
- return $grouped_types;
987
  }
988
 
989
  /**
@@ -1013,51 +992,21 @@ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInte
1013
  /**
1014
  * Updates records in DB matching a query.
1015
  *
1016
- * @param string $table Table name
1017
- * @param array $data Data to update (in column => value pairs).
1018
- * Both $data columns and $data values should be "raw" (neither should be SQL escaped).
1019
- * Sending a null value will cause the column to be set to NULL - the corresponding
1020
- * format is ignored in this case.
1021
- * @param array $where A named array of WHERE clauses (in column => value pairs).
1022
  * Multiple clauses will be joined with ANDs.
1023
  * Both $where columns and $where values should be "raw".
1024
- * Sending a null value will create an IS NULL comparison - the corresponding format will be ignored in this case.
 
 
1025
  * @return int|false The number of rows updated, or false on error.
1026
  * @since 4.1.3
1027
  */
1028
  public function UpdateQuery( $table, $data, $where ) {
1029
  return $this->connection->update( $table, $data, $where );
1030
  }
1031
-
1032
- /**
1033
- * @inheritDoc
1034
- */
1035
- public function GetModel() {
1036
- // implement in subclass
1037
- }
1038
-
1039
- /**
1040
- * @param array $data
1041
- *
1042
- * @return string
1043
- * @since 4.3.2
1044
- */
1045
- protected function formatArrayForQuery( $data ) {
1046
- return "'" . implode( ',', $data ) . "'";
1047
- }
1048
-
1049
- /**
1050
- * @param array $data
1051
- *
1052
- * @return string
1053
- * @since 4.3.2
1054
- */
1055
- protected function formatArrayForQueryRegex( $data ) {
1056
- $result = array();
1057
- foreach ( $data as $item ) {
1058
- array_push( $result, esc_sql( preg_quote( $item ) ) );
1059
- }
1060
-
1061
- return "'" . implode( '|', $result ) . "'";
1062
- }
1063
  }
17
  *
18
  * MySQL generic table used for Save, Read, Create or Delete
19
  * elements in the Database.
20
+ *
21
  * There are also the functions used in the Report Add-On to get the reports.
22
  *
23
  * @package wsal
45
  */
46
  protected $_idkey = '';
47
 
48
+ /**
49
+ * Local cache for a list of columns.
50
+ *
51
+ * @var string[]
52
+ */
53
+ protected $_column_cache = [];
54
+
55
  /**
56
  * Method: Constructor.
57
  *
58
+ * @param object $connection - DB connection object.
59
  */
60
+ public function __construct( $connection ) {
61
+ $this->connection = $connection;
62
  }
63
 
64
  /**
65
+ * Works out the grouping data entities for given statistical report type.
66
  *
67
+ * @param int $statistics_report_type Statistical report type.
68
+ * @param string $grouping_period Period to use for data grouping.
69
+ *
70
+ * @return string[]|null
71
+ * @since 4.4.0
72
  */
73
+ public static function get_grouping( $statistics_report_type, $grouping_period ) {
74
+ $grouping = null;
75
+ if ( ! is_null( $statistics_report_type ) ) {
76
+ $grouping = array( 'site' );
77
+ if ( ! is_null( $grouping_period ) ) {
78
+ array_push( $grouping, $grouping_period );
79
+ }
80
+
81
+ switch ( $statistics_report_type ) {
82
+ case WSAL_Rep_Common::DIFFERENT_IP:
83
+ array_push( $grouping, 'users' );
84
+ array_push( $grouping, 'ips' );
85
+ break;
86
+ case WSAL_Rep_Common::ALL_IPS:
87
+ array_push( $grouping, 'ips' );
88
+ break;
89
+ case WSAL_Rep_Common::LOGIN_ALL:
90
+ case WSAL_Rep_Common::LOGIN_BY_USER:
91
+ case WSAL_Rep_Common::LOGIN_BY_ROLE:
92
+ case WSAL_Rep_Common::PUBLISHED_ALL:
93
+ case WSAL_Rep_Common::PUBLISHED_BY_USER:
94
+ case WSAL_Rep_Common::PUBLISHED_BY_ROLE:
95
+ case WSAL_Rep_Common::ALL_USERS:
96
+ array_push( $grouping, 'users' );
97
+ break;
98
+
99
+ case WSAL_Rep_Common::VIEWS_ALL:
100
+ array_push( $grouping, 'posts' );
101
+ break;
102
+
103
+ case WSAL_Rep_Common::VIEWS_BY_USER:
104
+ case WSAL_Rep_Common::VIEWS_BY_ROLE:
105
+ array_push( $grouping, 'users' );
106
+ array_push( $grouping, 'posts' );
107
+ break;
108
+ }
109
+ }
110
+
111
+ return $grouping;
112
  }
113
 
114
  /**
115
+ * Method: Get connection.
116
  *
117
+ * @return object – DB connection object.
118
  */
119
+ public function get_connection() {
120
+ return $this->connection;
 
121
  }
122
 
123
  /**
127
  */
128
  public function GetWPTable() {
129
  global $wpdb;
130
+
131
  return $wpdb->base_prefix . $this->_table;
132
  }
133
 
134
  /**
135
+ * @inheritDoc
136
+ */
137
+ public function Install() {
138
+ $_wpdb = $this->connection;
139
+ $_wpdb->query( $this->_GetInstallQuery() );
140
+ }
141
+
142
+ /**
143
+ * Table install query.
144
+ *
145
+ * @param string|false $prefix - (Optional) Table prefix.
146
+ *
147
+ * @return string - Must return SQL for creating table.
148
+ */
149
+ protected function _GetInstallQuery( $prefix = false ) {
150
+ $_wpdb = $this->connection;
151
+ $class = get_class( $this );
152
+ $copy = new $class( $this->connection );
153
+ $table_name = $this->GetTable();
154
+ $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
155
+ $cols = $this->GetColumns();
156
+ foreach ( $cols as $key ) {
157
+ $sql .= $this->_GetSqlColumnDefinition( $copy, $key );
158
+ }
159
+
160
+ $sql .= $this->GetTableOptions() . PHP_EOL;
161
+ $sql .= ') ' . $_wpdb->get_charset_collate();
162
+
163
+ return $sql;
164
+ }
165
+
166
+ /**
167
+ * Returns table name.
168
  *
169
  * @return string
170
  */
171
+ public function GetTable() {
172
+ $_wpdb = $this->connection;
173
+
174
+ return $_wpdb->base_prefix . $this->_table;
175
  }
176
 
177
  /**
182
  public function GetColumns() {
183
  $model = $this->GetModel();
184
 
185
+ if ( empty( $this->_column_cache ) ) {
186
  $this->_column_cache = array();
187
  foreach ( array_keys( get_object_vars( $model ) ) as $col ) {
188
  if ( trim( $col ) && $col[0] != '_' ) {
190
  }
191
  }
192
  }
193
+
194
  return $this->_column_cache;
195
  }
196
 
197
  /**
198
+ * @inheritDoc
 
 
 
199
  */
200
+ public function GetModel() {
201
+ return new WSAL_Models_Query();
202
+ }
 
 
 
 
203
 
204
+ /**
205
+ * Generate SQL column definition string for the CREATE TABLE statement.
206
+ *
207
+ * @param object $copy
208
+ * @param string $key
209
+ *
210
+ * @return string
211
+ */
212
+ protected function _GetSqlColumnDefinition( $copy, $key ) {
213
+ $result = ' ';
214
+ switch ( true ) {
215
+ case ( $key === $this->_idkey ):
216
+ $result .= $key . ' BIGINT NOT NULL AUTO_INCREMENT,' . PHP_EOL;
217
+ break;
218
+ case is_int( $copy->$key ):
219
+ $result .= $key . ' BIGINT NOT NULL,' . PHP_EOL;
220
+ break;
221
+ case is_float( $copy->$key ):
222
+ $result .= $key . ' DOUBLE NOT NULL,' . PHP_EOL;
223
+ break;
224
+ case is_string( $copy->$key ):
225
+ $maxlength = $key . '_maxlength';
226
+ if ( property_exists( $copy, $maxlength ) ) {
227
+ // The double `$$` is intentional.
228
+ $result .= $key . ' VARCHAR(' . (int) $copy::$$maxlength . ') NOT NULL,' . PHP_EOL;
229
+ } else {
230
+ $result .= $key . ' LONGTEXT NOT NULL,' . PHP_EOL;
231
+ }
232
+ break;
233
+ case is_bool( $copy->$key ):
234
+ $result .= $key . ' BIT NOT NULL,' . PHP_EOL;
235
+ break;
236
+ case is_array( $copy->$key ):
237
+ case is_object( $copy->$key ):
238
+ $result .= $key . ' LONGTEXT NOT NULL,' . PHP_EOL;
239
+ break;
240
+ default:
241
+ // fallback for any other columns would go here
242
+ break;
243
  }
244
+
245
+ return $result;
246
  }
247
 
248
  /**
249
+ * SQL table options (constraints, foreign keys, indexes etc).
250
+ *
251
+ * @return string
252
  */
253
+ protected function GetTableOptions() {
254
+ return ' PRIMARY KEY (' . $this->_idkey . ')';
 
255
  }
256
 
257
  /**
263
  }
264
 
265
  /**
266
+ * @inheritDoc
267
  */
268
  public function Uninstall() {
269
  $_wpdb = $this->connection;
284
 
285
  // Query table exists.
286
  $table_exists_query = "SHOW TABLES LIKE '" . $this->GetTable() . "'";
287
+
288
  return $_wpdb->query( $table_exists_query );
289
  }
290
 
291
  /**
292
+ * Must return SQL for removing table (at a minimum, it should be ` 'DROP TABLE ' . $this->_table `).
293
  *
294
+ * @return string
295
+ */
296
+ protected function _GetUninstallQuery() {
297
+ return 'DROP TABLE IF EXISTS ' . $this->GetTable();
298
+ }
299
+
300
+ /**
301
+ * @inheritDoc
302
  */
303
  public function Save( $active_record ) {
304
  $_wpdb = $this->connection;
306
  $data = array();
307
  $format = array();
308
 
309
+ $columns = $this->GetColumns();
310
+ foreach ( $columns as $index => $key ) {
311
  if ( $key == $this->_idkey ) {
312
  $_id_index = $index;
313
  }
317
  if ( is_int( $copy->$key ) ) {
318
  $deffmt = '%d';
319
  }
320
+
321
  if ( is_float( $copy->$key ) ) {
322
  $deffmt = '%f';
323
  }
324
+
325
  if ( is_array( $copy->$key ) || is_object( $copy->$key ) ) {
326
  $data[ $key ] = WSAL_Helpers_DataHelper::JsonEncode( $val );
327
  } else {
328
  $data[ $key ] = $val;
329
  }
330
+
331
  $format[] = $deffmt;
332
  }
333
 
341
  if ( false !== $result && $_wpdb->insert_id ) {
342
  $copy->setId( $_wpdb->insert_id );
343
  }
344
+
345
  return $result;
346
  }
347
 
349
  * Load record from DB (Single row).
350
  *
351
  * @param string $cond - (Optional) Load condition.
352
+ * @param array $args - (Optional) Load condition arguments.
353
  *
354
  * @return array
355
  */
356
  public function Load( $cond = '%d', $args = array( 1 ) ) {
357
  $_wpdb = $this->connection;
358
  $sql = $_wpdb->prepare( 'SELECT * FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
359
+
360
  return $_wpdb->get_row( $sql, ARRAY_A );
361
  }
362
 
364
  * Load records from DB (Multi rows).
365
  *
366
  * @param string $cond Load condition.
367
+ * @param array $args (Optional) Load condition arguments.
368
  *
369
  * @return array
370
  * @throws Exception
376
  foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
377
  $result[] = $this->getModel()->LoadData( $data );
378
  }
379
+
380
  return $result;
381
  }
382
 
383
  /**
384
+ * @inheritDoc
 
 
 
385
  */
386
  public function Delete( $active_record ) {
387
+ $_wpdb = $this->connection;
388
+
389
  return $_wpdb->delete(
390
  $this->GetTable(),
391
+ array(
392
+ $this->_idkey => $active_record->getId(),
393
+ ),
394
+ array( '%d' )
395
  );
396
  }
397
 
399
  * Delete records in DB matching a query.
400
  *
401
  * @param string $query Full SQL query.
402
+ * @param array $args (Optional) Query arguments.
403
  *
404
  * @return int|bool
405
  */
406
  public function DeleteQuery( $query, $args = array() ) {
407
+ $_wpdb = $this->connection;
408
+ $sql = count( $args ) ? $_wpdb->prepare( $query, $args ) : $query;
409
+
410
  return $_wpdb->query( $sql );
411
  }
412
 
413
  /**
414
+ * @inheritDoc
 
 
 
 
 
 
415
  */
416
  public function LoadMulti( $cond, $args = array() ) {
417
  $_wpdb = $this->connection;
422
  foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
423
  $result[] = $this->getModel()->LoadData( $data );
424
  }
425
+
426
  return $result;
427
  }
428
 
429
  /**
430
+ * @inheritDoc
 
 
 
 
 
431
  */
432
  public function LoadAndCallForEach( $callback, $cond = '%d', $args = array( 1 ) ) {
433
  $_wpdb = $this->connection;
439
  }
440
 
441
  /**
442
+ * @inheritDoc
 
 
 
 
 
443
  */
444
  public function Count( $cond = '%d', $args = array( 1 ) ) {
445
  $_wpdb = $this->connection;
446
  $sql = $_wpdb->prepare( 'SELECT COUNT(*) FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
447
+
448
  return (int) $_wpdb->get_var( $sql );
449
  }
450
 
452
  * Count records in the DB matching a query.
453
  *
454
  * @param string $query Full SQL query.
455
+ * @param array $args (Optional) Query arguments.
456
+ *
457
  * @return int Number of matching records.
458
  */
459
  public function CountQuery( $query, $args = array() ) {
460
  $_wpdb = $this->connection;
461
  $sql = count( $args ) ? $_wpdb->prepare( $query, $args ) : $query;
462
+
463
  return (int) $_wpdb->get_var( $sql );
464
  }
465
 
466
  /**
467
+ * @inheritDoc
 
 
 
 
 
 
468
  */
469
  public function LoadMultiQuery( $query, $args = array() ) {
470
  $_wpdb = $this->connection;
473
  foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
474
  $result[] = $this->getModel()->LoadData( $data );
475
  }
476
+
477
  return $result;
478
  }
479
 
480
  /**
481
+ * Retrieves report data for generic (alerts based) report. Function used in WSAL reporting extension.
482
  *
483
+ * @param WSAL_ReportArgs $report_args Report arguments.
484
+ * @param int $next_date (Optional) Created on >.
485
+ * @param int $limit (Optional) Limit.
486
+ * @param int $statistics_report_type Statistics report type.
487
+ * @param string $grouping_period Period to use for data grouping.
488
+ *
489
+ * @return array Report results
490
  */
491
+ public function get_report_data( $report_args, $next_date = null, $limit = 0, $statistics_report_type = null, $grouping_period = null ) {
 
 
 
 
 
492
 
493
+ // Figure out the grouping statement and the columns' selection.
494
+ $grouping = self::get_grouping( $statistics_report_type, $grouping_period );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
 
496
+ // Build the SQL query and runs it.
497
+ $query = $this->build_reporting_query( $report_args, false, $grouping, $next_date, $limit );
498
 
499
+ // Statistical reports expect data as array, regular reports use objects.
500
+ $result_format = is_null( $statistics_report_type ) ? OBJECT : ARRAY_A;
501
+ $results = $this->connection->get_results( $query, $result_format );
502
 
503
+ if ( ! empty( $results ) ) {
504
+ $last_item = end( $results );
505
+ if ( is_object( $last_item ) && property_exists( $last_item, 'created_on' ) ) {
506
+ $results['lastDate'] = $last_item->created_on;
507
+ } elseif ( is_array( $last_item ) && array_key_exists( 'created_on', $last_item ) ) {
508
+ $results['lastDate'] = $last_item['created_on'];
509
+ }
510
  }
511
 
512
+ return $results;
513
  }
514
 
515
  /**
516
+ * Builds an SQL query for the main report.
 
517
  *
518
+ * @param WSAL_ReportArgs $report_args Report arguments.
519
+ * @param bool $count_only If true, the resulting query will only provide a count of matching entries
520
+ * is
521
+ * returned.
522
+ * @param array $grouping Grouping criteria. Drives the selected columns as well as the GROUP BY
523
+ * statement. Only null value prevents grouping. Empty array means to group by
524
+ * time period only.
525
+ * @param int $next_date (Optional) Created on >.
526
+ * @param int $limit (Optional) Limit.
 
 
527
  *
528
  * @return string
529
  */
530
+ private function build_reporting_query( $report_args, $count_only, $grouping = null, $next_date = null, $limit = 0 ) {
531
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
532
+ $table_occ = $occurrence->GetTable();
533
+
534
+ if ( $count_only ) {
535
+ $select_fields = array( 'COUNT(1) as count' );
536
+ $group_by = array( 'occ.id' );
537
+ } elseif ( is_null( $grouping ) ) {
538
+ $select_fields = array(
539
+ "occ.id",
540
+ "occ.alert_id",
541
+ "occ.site_id",
542
+ "occ.created_on",
543
+ "replace( replace( replace( occ.user_roles, '[', ''), ']', ''), '\\'', '') AS roles",
544
+ "occ.client_ip AS ip",
545
+ "occ.user_agent AS ua",
546
+ "COALESCE( occ.username, occ.user_id ) as user_id",
547
+ "occ.object",
548
+ "occ.event_type",
549
+ "occ.post_id",
550
+ "occ.post_type",
551
+ "occ.post_status",
552
+ );
553
+ } else {
554
+ $select_fields = array();
555
+ $group_by = array();
556
+ foreach ( $grouping as $grouping_item ) {
557
+ switch ($grouping_item) {
558
+ case 'site':
559
+ array_push( $select_fields, 'site_id' );
560
+ array_push( $group_by, 'site_id' );
561
+ break;
562
+ case 'users':
563
+ array_push( $select_fields, "COALESCE( occ.username, occ.user_id ) as user" );
564
+ array_push( $group_by, 'user' );
565
+ break;
566
+ case 'posts':
567
+ array_push( $select_fields, 'post_id' );
568
+ array_push( $group_by, 'post_id' );
569
+ break;
570
+ case 'day':
571
+ array_push( $select_fields, 'DATE_FORMAT( FROM_UNIXTIME( occ.created_on ), "%Y-%m-%d" ) AS period' );
572
+ array_push( $group_by, 'period' );
573
+ break;
574
+ case 'week':
575
+ array_push( $select_fields, 'DATE_FORMAT( FROM_UNIXTIME( occ.created_on ), "%Y-%u" ) AS period' );
576
+ array_push( $group_by, 'period' );
577
+ break;
578
+ case 'month':
579
+ array_push( $select_fields, 'DATE_FORMAT( FROM_UNIXTIME( occ.created_on ), "%Y-%m" ) AS period' );
580
+ array_push( $group_by, 'period' );
581
+ break;
582
+ }
583
+ }
584
 
585
+ array_push( $select_fields, 'COUNT(*) as count' );
586
+ }
 
 
 
 
 
 
587
 
588
+ $sql = 'SELECT ' . implode( ',', $select_fields ) . ' FROM ' . $table_occ . ' AS occ ';
589
+
590
+ $sql .= $this->build_where_statement( $report_args );
591
+ if ( ! empty( $next_date ) ) {
592
+ $sql .= ' AND occ.created_on < ' . $next_date;
 
 
 
 
 
593
  }
594
+
595
+ if ( isset( $group_by ) && ! empty( $group_by ) ) {
596
+ $sql .= " GROUP BY " . implode( ',', $group_by );
597
+ $orderby_parts = array_map( function ( $item ) {
598
+ return 'period' === $item ? $item . ' DESC ' : $item . ' ASC ';
599
+ }, $group_by );
600
+
601
+ $sql .= " ORDER BY " . implode( ',', $orderby_parts );
602
+ } else {
603
+ $sql .= " ORDER BY created_on DESC ";
604
+ }
605
+
606
+ if ( ! empty( $limit ) ) {
607
+ $sql .= " LIMIT {$limit}";
608
+ }
609
+
610
+ return $sql;
611
  }
612
 
613
  /**
614
+ * Generates SQL where statement based on given report args.
615
  *
616
  * @param WSAL_ReportArgs $report_args Report arguments.
 
 
617
  *
618
+ * @return string
619
  */
620
+ private function build_where_statement( $report_args ) {
621
+ $_site_id = null;
 
 
 
 
 
 
 
 
 
 
 
 
622
  $sites_negate_expression = '';
623
  if ( $report_args->site__in ) {
624
  $_site_id = $this->formatArrayForQuery( $report_args->site__in );
625
+ } elseif ( $report_args->site__not_in ) {
626
  $_site_id = $this->formatArrayForQuery( $report_args->site__not_in );
627
  $sites_negate_expression = 'NOT';
628
  }
629
 
630
+ $_user_id = null;
631
  $users_negate_expression = '';
 
632
  if ( $report_args->user__in ) {
633
  $_user_id = $this->formatArrayForQuery( $report_args->user__in );
634
+ } elseif ( $report_args->user__not_in ) {
635
  $_user_id = $this->formatArrayForQuery( $report_args->user__not_in );
636
  $users_negate_expression = 'NOT';
 
637
  }
638
 
639
  $user_names = $this->GetUserNames( $_user_id );
640
 
641
+ $_role_name = null;
642
  $roles_negate_expression = '';
643
  if ( $report_args->role__in ) {
644
  $_role_name = $this->formatArrayForQueryRegex( $report_args->role__in );
645
+ } elseif ( $report_args->role__not_in ) {
646
  $_role_name = $this->formatArrayForQueryRegex( $report_args->role__not_in );
647
  $roles_negate_expression = 'NOT';
648
  }
649
 
650
+ $_alert_code = null;
651
+ $alert_code_negate_expression = '';
652
  if ( $report_args->code__in ) {
653
  $_alert_code = $this->formatArrayForQuery( $report_args->code__in );
654
+ } elseif ( $report_args->code__not_in ) {
655
+ $_alert_code = $this->formatArrayForQuery( $report_args->code__not_in );
656
+ $alert_code_negate_expression = 'NOT';
657
  }
658
 
659
+ $_post_ids = null;
660
+ $post_ids_negate_expression = '';
661
+ if ( $report_args->post__in ) {
662
+ $_post_ids = $this->formatArrayForQueryRegex( $report_args->post__in );
663
+ } elseif ( $report_args->post__not_in ) {
664
+ $_post_ids = $this->formatArrayForQueryRegex( $report_args->post__not_in );
665
+ $post_ids_negate_expression = 'NOT';
666
+ }
667
+
668
+ $_post_types = null;
669
+ $post_types_negate_expression = '';
670
  if ( $report_args->post_type__in ) {
671
  $_post_types = $this->formatArrayForQueryRegex( $report_args->post_type__in );
672
+ } elseif ( $report_args->post_type__not_in ) {
673
+ $_post_types = $this->formatArrayForQueryRegex( $report_args->post_type__not_in );
674
+ $post_types_negate_expression = 'NOT';
675
  }
676
 
677
+ $_post_statuses = null;
678
+ $post_statuses_negate_expression = '';
679
  if ( $report_args->post_status__in ) {
680
  $_post_statuses = $this->formatArrayForQueryRegex( $report_args->post_status__in );
681
+ } else if ( $report_args->post_status__not_in ) {
682
+ $_post_statuses = $this->formatArrayForQueryRegex( $report_args->post_status__not_in );
683
+ $post_statuses_negate_expression = 'NOT';
684
  }
685
 
686
+ $_ip_addresses = null;
687
+ $ip_addresses_negate_expression = '';
688
+ if ( $report_args->ip__in ) {
689
+ $_ip_addresses = $this->formatArrayForQuery( $report_args->ip__in );
690
+ } else if ( $report_args->ip__not_in ) {
691
+ $_ip_addresses = $this->formatArrayForQuery( $report_args->ip__not_in );
692
+ $ip_addresses_negate_expression = 'NOT';
693
+ }
694
+
695
+ $_objects = null;
696
  $objects_negate_expression = '';
697
  if ( $report_args->object__in ) {
698
  $_objects = $this->formatArrayForQuery( $report_args->object__in );
699
+ } elseif ( $report_args->object__not_in ) {
700
  $_objects = $this->formatArrayForQuery( $report_args->object__not_in );
701
  $objects_negate_expression = 'NOT';
702
  }
703
 
704
+ $_event_types = null;
705
  $event_types_negate_expression = '';
706
  if ( $report_args->type__in ) {
707
  $_event_types = $this->formatArrayForQuery( $report_args->type__in );
708
+ } elseif ( $report_args->type__not_in ) {
709
+ $_event_types = $this->formatArrayForQuery( $report_args->type__not_in );
710
  $event_types_negate_expression = 'NOT';
711
  }
712
 
713
+ $_start_timestamp = null;
714
  if ( $report_args->start_date ) {
715
  $start_datetime = DateTime::createFromFormat( 'Y-m-d H:i:s', $report_args->start_date . ' 00:00:00' );
716
  $_start_timestamp = $start_datetime->format( 'U' );
717
  }
718
 
719
+ $_end_timestamp = null;
720
  if ( $report_args->end_date ) {
721
  $end_datetime = DateTime::createFromFormat( 'Y-m-d H:i:s', $report_args->end_date . ' 23:59:59' );
722
  $_end_timestamp = $end_datetime->format( 'U' );
723
  }
724
 
725
+ $users_condition_parts = array();
726
+ if ( ! is_null( $_user_id ) ) {
727
+ array_push( $users_condition_parts, " {$users_negate_expression} find_in_set( occ.user_id, $_user_id ) > 0 " );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
728
  }
729
 
730
+ if ( ! is_null( $user_names ) ) {
731
+ array_push( $users_condition_parts, " {$users_negate_expression} replace( occ.username, '\"', '' ) IN ( $user_names ) " );
 
 
 
 
 
 
 
 
 
 
 
732
  }
733
+
734
+ $where_statement = " WHERE 1 = 1 ";
735
+
736
+ if ( ! empty( $users_condition_parts ) ) {
737
+ $where_statement .= ' AND ( ' . implode( 'OR', $users_condition_parts ) . ' ) ';
738
  }
739
 
740
+ if ( ! is_null( $_site_id ) ) {
741
+ $where_statement .= " AND {$sites_negate_expression} find_in_set( occ.site_id, {$_site_id} ) > 0 ";
742
+ }
743
+
744
+ if ( ! is_null( $_role_name ) ) {
745
+ $where_statement .= " AND user_roles {$roles_negate_expression} REGEXP {$_role_name} ";
746
+ }
747
+
748
+ if ( ! is_null( $_ip_addresses ) ) {
749
+ $where_statement .= " AND {$ip_addresses_negate_expression} find_in_set( occ.client_ip, {$_ip_addresses} ) > 0 ";
750
+ }
751
+
752
+ if ( ! is_null( $_objects ) ) {
753
+ $where_statement .= " AND {$objects_negate_expression} find_in_set( occ.object, {$_objects} ) > 0 ";
754
+ }
755
+
756
+ if ( ! is_null( $_event_types ) ) {
757
+ $where_statement .= " AND {$event_types_negate_expression} find_in_set( occ.event_type, {$_event_types} ) > 0 ";
758
+ }
759
+
760
+ if ( ! is_null( $_alert_code ) ) {
761
+ $where_statement .= " AND {$alert_code_negate_expression} find_in_set( occ.alert_id, {$_alert_code} ) > 0 ";
762
+ }
763
+
764
+ if ( ! is_null( $_post_ids ) ) {
765
+ $where_statement .= " AND {$post_ids_negate_expression} find_in_set( occ.post_id, {$_post_ids} ) > 0 ";
766
+ }
767
+
768
+ if ( ! is_null( $_post_statuses ) ) {
769
+ $where_statement .= " AND {$post_statuses_negate_expression} find_in_set( occ.post_status, {$_post_statuses} ) > 0 ";
770
+ }
771
+
772
+ if ( ! is_null( $_post_types ) ) {
773
+ $where_statement .= " AND {$post_types_negate_expression} find_in_set( occ.post_type, {$_post_types} ) > 0 ";
774
+ }
775
+
776
+ if ( ! is_null( $_start_timestamp ) ) {
777
+ $where_statement .= " AND occ.created_on >= {$_start_timestamp} ";
778
+ }
779
+
780
+ if ( ! is_null( $_end_timestamp ) ) {
781
+ $where_statement .= " AND occ.created_on <= {$_end_timestamp} ";
782
+ }
783
+
784
+ return $where_statement;
785
  }
786
 
787
  /**
788
+ * @param array $data
 
789
  *
790
+ * @return string
791
+ * @since 4.3.2
792
  */
793
+ protected function formatArrayForQuery( $data ) {
794
+ return "'" . implode( ',', $data ) . "'";
795
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
796
 
797
+ /**
798
+ * Get Users user_login.
799
+ *
800
+ * @param int $_user_id - User ID.
801
+ *
802
+ * @return string comma separated users login
803
+ */
804
+ private function GetUserNames( $_user_id ) {
805
+ global $wpdb;
806
 
807
+ $user_names = null;
808
+ if ( ! empty( $_user_id ) && 'null' != $_user_id && ! is_null( $_user_id ) ) {
809
+ $sql = 'SELECT user_login FROM ' . $wpdb->users . ' WHERE find_in_set(ID, @userId) > 0';
810
+ $wpdb->query( "SET @userId = $_user_id" );
811
+ $result = $wpdb->get_results( $sql, ARRAY_A );
812
+ $users_array = array();
813
+ foreach ( $result as $item ) {
814
+ $users_array[] = '"' . $item['user_login'] . '"';
815
+ }
816
+ $user_names = implode( ', ', $users_array );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817
  }
818
 
819
+ return $user_names;
820
+ }
 
 
 
 
 
 
 
821
 
822
+ /**
823
+ * @param array $data
824
+ *
825
+ * @return string
826
+ * @since 4.3.2
827
+ */
828
+ protected function formatArrayForQueryRegex( $data ) {
829
+ $result = array();
830
+ foreach ( $data as $item ) {
831
+ array_push( $result, esc_sql( preg_quote( $item ) ) );
832
+ }
833
+
834
+ return "'" . implode( '|', $result ) . "'";
835
  }
836
 
837
  /**
838
  * Function used in WSAL reporting extension.
839
+ * Check if criteria are matching in the DB.
840
  *
841
+ * @param WSAL_ReportArgs $report_args - Query conditions.
 
842
  *
843
+ * @return int count of distinct values
844
  */
845
+ public function CheckMatchReportCriteria( $report_args ) {
846
+ $query = $this->build_reporting_query( $report_args, true );
847
 
848
+ return (int) $this->connection->get_var( $query );
849
+ }
850
+
851
+ /**
852
+ * Retrieves report data for IP address based reports. Function is used in WSAL reporting extension.
853
+ *
854
+ * @param WSAL_ReportArgs $report_args Report arguments.
855
+ * @param int $limit (Optional) Limit.
856
+ * @param int $statistics_report_type Statistics report type.
857
+ * @param string $grouping_period Period to use for data grouping.
858
+ *
859
+ * @return array Raw report results as objects. Content depends on the report type.
860
+ */
861
+ public function get_ip_address_report_data( $report_args, $limit = 0, $statistics_report_type = null, $grouping_period = null ) {
862
  global $wpdb;
863
  $_wpdb = $this->connection;
 
864
 
865
  // Tables.
866
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $_wpdb );
867
+ $table_occ = $occurrence->GetTable();
868
+
 
869
  // Get temp table `wsal_tmp_users`.
870
+ $tmp_users = new WSAL_Adapters_MySQL_TmpUser( $_wpdb );
871
  // If the table exist.
872
  if ( $tmp_users->IsInstalled() ) {
873
+ $table_users = $tmp_users->GetTable();
874
  $this->TempUsers( $table_users );
875
  } else {
876
  $table_users = $wpdb->users;
877
  }
878
 
879
+ // Figure out the grouping statement and the columns' selection.
880
+ $grouping = self::get_grouping( $statistics_report_type, $grouping_period );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
881
 
882
+ // Figure out the selected columns and group by statement.
883
+ $group_by_columns = array(
884
+ 'site_id',
885
+ );
 
 
 
 
886
 
887
+ if ( in_array( 'users', $grouping, true ) ) {
888
+ array_push( $group_by_columns, 'username' );
 
889
  }
890
 
891
+ if ( in_array( 'ips', $grouping, true ) ) {
892
+ array_push( $group_by_columns, 'client_ip' );
 
 
 
 
 
893
  }
894
 
895
+ $select_fields = $group_by_columns;
896
+ foreach ( $grouping as $grouping_item ) {
897
+ switch ( $grouping_item ) {
898
+ case 'day':
899
+ array_unshift( $select_fields, 'DATE_FORMAT( FROM_UNIXTIME( created_on ), "%Y-%m-%d" ) AS period' );
900
+ array_unshift( $group_by_columns, 'period' );
901
+ break;
902
+ case 'week':
903
+ array_unshift( $select_fields, 'DATE_FORMAT( FROM_UNIXTIME( created_on ), "%Y-%u" ) AS period' );
904
+ array_unshift( $group_by_columns, 'period' );
905
+ break;
906
+ case 'month':
907
+ array_unshift( $select_fields, 'DATE_FORMAT( FROM_UNIXTIME( created_on ), "%Y-%m" ) AS period' );
908
+ array_unshift( $group_by_columns, 'period' );
909
+ break;
910
+ }
911
  }
912
 
913
+ $where_statement = $this->build_where_statement( $report_args );
914
 
915
+ $sql = "
916
+ SELECT " . implode( ',', $select_fields ) . " FROM (
917
+ SELECT occ.created_on, occ.site_id, occ.username, occ.client_ip
 
 
 
 
 
 
 
 
 
 
918
  FROM $table_occ AS occ
919
+ {$where_statement}
920
+ HAVING username IS NOT NULL AND username NOT IN ( 'Unregistered user', 'Plugins', 'Plugin')
921
+ UNION ALL
922
+ SELECT occ.created_on, occ.site_id, u.user_login as username, occ.client_ip
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
923
  FROM $table_occ AS occ
924
+ JOIN $table_users AS u ON u.ID = occ.user_id
925
+ {$where_statement}
926
+ HAVING username IS NOT NULL AND username NOT IN ( 'Unregistered user', 'Plugins', 'Plugin')
927
+ ) ip_logins
928
+ GROUP BY " . implode( ',', $group_by_columns );
929
+
930
+ $orderby_parts = array_map( function ( $item ) {
931
+ return 'period' === $item ? $item . ' DESC ' : $item . ' ASC ';
932
+ }, $group_by_columns );
933
+
934
+ $sql .= " ORDER BY " . implode( ',', $orderby_parts );
935
+
936
+ if ( ! empty( $limit ) ) {
937
+ $sql .= " LIMIT {$limit}";
 
 
 
 
 
 
 
938
  }
939
 
940
+ $results = $_wpdb->get_results( $sql, ARRAY_A );
941
+ if ( is_array( $results ) && ! empty( $results ) ) {
942
+ return $results;
943
+ }
 
 
 
 
 
 
 
944
 
945
+ return array();
946
+ }
 
 
 
 
 
 
947
 
948
+ /**
949
+ * @inheritDoc
950
+ */
951
+ public function IsInstalled() {
952
+ $_wpdb = $this->connection;
953
+ $sql = "SHOW TABLES LIKE '" . $this->GetTable() . "'";
954
+
955
+ // Table transient.
956
+ $wsal_table_transient = 'wsal_' . strtolower( $this->GetTable() ) . '_status';
957
+ $wsal_db_table_status = get_transient( $wsal_table_transient );
958
+
959
+ // If transient does not exist, then run SQL query.
960
+ if ( ! $wsal_db_table_status ) {
961
+ $wsal_db_table_status = strtolower( $_wpdb->get_var( $sql ) ) == strtolower( $this->GetTable() );
962
+ set_transient( $wsal_table_transient, $wsal_db_table_status, DAY_IN_SECONDS );
963
  }
964
 
965
+ return $wsal_db_table_status;
966
  }
967
 
968
  /**
992
  /**
993
  * Updates records in DB matching a query.
994
  *
995
+ * @param string $table Table name
996
+ * @param array $data Data to update (in column => value pairs).
997
+ * Both $data columns and $data values should be "raw" (neither should be SQL
998
+ * escaped). Sending a null value will cause the column to be set to NULL - the
999
+ * corresponding format is ignored in this case.
1000
+ * @param array $where A named array of WHERE clauses (in column => value pairs).
1001
  * Multiple clauses will be joined with ANDs.
1002
  * Both $where columns and $where values should be "raw".
1003
+ * Sending a null value will create an IS NULL comparison - the corresponding
1004
+ * format will be ignored in this case.
1005
+ *
1006
  * @return int|false The number of rows updated, or false on error.
1007
  * @since 4.1.3
1008
  */
1009
  public function UpdateQuery( $table, $data, $where ) {
1010
  return $this->connection->update( $table, $data, $where );
1011
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1012
  }
classes/Adapters/MySQL/MetaAdapter.php CHANGED
@@ -72,12 +72,15 @@ class WSAL_Adapters_MySQL_Meta extends WSAL_Adapters_MySQL_ActiveRecord implemen
72
  public $value = array(); // Force mixed type.
73
 
74
  /**
75
- * Returns the model class for adapter.
76
  *
77
  * @return WSAL_Models_Meta
78
  */
79
  public function GetModel() {
80
- return new WSAL_Models_Meta();
 
 
 
81
  }
82
 
83
  /**
@@ -90,6 +93,9 @@ class WSAL_Adapters_MySQL_Meta extends WSAL_Adapters_MySQL_ActiveRecord implemen
90
  . ' KEY occurrence_name (occurrence_id,name)';
91
  }
92
 
 
 
 
93
  public function DeleteByOccurrenceIds( $occurrence_ids ) {
94
  if ( ! empty( $occurrence_ids ) ) {
95
  $sql = 'DELETE FROM ' . $this->GetTable() . ' WHERE occurrence_id IN (' . implode( ',', $occurrence_ids ) . ')';
@@ -98,27 +104,19 @@ class WSAL_Adapters_MySQL_Meta extends WSAL_Adapters_MySQL_ActiveRecord implemen
98
  }
99
  }
100
 
101
- public function LoadByNameAndOccurrenceId( $meta_name, $occurrence_id ) {
102
- return $this->Load( 'occurrence_id = %d AND name = %s', array( $occurrence_id, $meta_name ) );
103
- }
104
-
105
  /**
106
- * Get distinct values of IPs.
107
- *
108
- * @param int $limit - (Optional) Limit.
109
- * @return array - Distinct values of IPs.
110
  */
111
- public function GetMatchingIPs( $limit = null ) {
112
- $_wpdb = $this->connection;
113
- $sql = "SELECT DISTINCT value FROM {$this->GetTable()} WHERE name = \"ClientIP\"";
114
- if ( ! is_null( $limit ) ) {
115
- $sql .= ' LIMIT ' . $limit;
116
- }
117
- $ips = $_wpdb->get_col( $sql );
118
- foreach ( $ips as $key => $ip ) {
119
- $ips[ $key ] = str_replace( '"', '', $ip );
120
  }
121
- return array_unique( $ips );
 
122
  }
123
 
124
  /**
72
  public $value = array(); // Force mixed type.
73
 
74
  /**
75
+ * @inheritDoc
76
  *
77
  * @return WSAL_Models_Meta
78
  */
79
  public function GetModel() {
80
+ $result = new WSAL_Models_Meta();
81
+ $result->setAdapter( $this );
82
+
83
+ return $result;
84
  }
85
 
86
  /**
93
  . ' KEY occurrence_name (occurrence_id,name)';
94
  }
95
 
96
+ /**
97
+ * @inheritDoc
98
+ */
99
  public function DeleteByOccurrenceIds( $occurrence_ids ) {
100
  if ( ! empty( $occurrence_ids ) ) {
101
  $sql = 'DELETE FROM ' . $this->GetTable() . ' WHERE occurrence_id IN (' . implode( ',', $occurrence_ids ) . ')';
104
  }
105
  }
106
 
 
 
 
 
107
  /**
108
+ * @inheritDoc
 
 
 
109
  */
110
+ public function LoadByNameAndOccurrenceId( $meta_name, $occurrence_id ) {
111
+ // make sure to grab the migrated meta fields from the occurrence table
112
+ if ( in_array( $meta_name, array_keys( WSAL_Models_Occurrence::$migrated_meta ) ) ) {
113
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->get_connection() );
114
+ $column_name = WSAL_Models_Occurrence::$migrated_meta[ $meta_name ];
115
+
116
+ return $occurrence->$column_name;
 
 
117
  }
118
+
119
+ return $this->Load( 'occurrence_id = %d AND name = %s', array( $occurrence_id, $meta_name ) );
120
  }
121
 
122
  /**
classes/Adapters/MySQL/OccurrenceAdapter.php CHANGED
@@ -22,9 +22,7 @@ if ( ! defined( 'ABSPATH' ) ) {
22
  class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_OccurrenceInterface {
23
 
24
  /**
25
- * Contains the table name.
26
- *
27
- * @var string
28
  */
29
  protected $_table = 'wsal_occurrences';
30
 
@@ -35,13 +33,6 @@ class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord im
35
  */
36
  protected $_idkey = 'id';
37
 
38
- /**
39
- * Meta data.
40
- *
41
- * @var WSAL_Models_Meta
42
- */
43
- protected $_meta;
44
-
45
  /**
46
  * Occurrence id.
47
  *
@@ -71,92 +62,141 @@ class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord im
71
  public $created_on = 0.0;
72
 
73
  /**
74
- * Is read?
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  *
76
- * @var bool
77
- * @deprecated 4.3.2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  */
79
- public $is_read = false;
80
 
81
  /**
82
- * Is migrated?
83
  *
84
- * @var bool
85
- * @deprecated 4.3.2
86
  */
87
- public $is_migrated = false;
88
 
89
  /**
90
- * SQL table options (constraints, foreign keys, indexes etc).
91
  *
92
- * @return string
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  */
94
  protected function GetTableOptions() {
95
  return parent::GetTableOptions() . ',' . PHP_EOL
96
- . ' KEY site_alert_created (site_id,alert_id,created_on)';
97
  }
98
 
99
  /**
100
- * Returns the model class for adapter.
101
  *
102
  * @return WSAL_Models_Occurrence
103
  */
104
  public function GetModel() {
105
- return new WSAL_Models_Occurrence();
106
- }
107
 
108
- /**
109
- * Returns metadata related to this event.
110
- *
111
- * @param object $occurence - Occurrence model instance.
112
- * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
113
- * @return WSAL_Models_Meta
114
- */
115
- public function GetMeta( $occurence ) {
116
- if ( ! isset( $this->_meta ) ) {
117
- $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
118
- $this->_meta = $meta->Load( 'occurrence_id = %d', array( $occurence->id ) );
119
- }
120
- return $this->_meta;
121
  }
122
 
123
  /**
124
- * Returns allmeta data related to this event.
125
- *
126
- * @param object $occurence - Occurrence model instance.
127
- * @see WSAL_Adapters_MySQL_ActiveRecord::LoadArray()
128
- * @return WSAL_Models_Meta[]
129
- */
130
- public function GetMultiMeta( $occurence ) {
131
- if ( ! isset( $this->_meta ) ) {
132
- $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
133
- $this->_meta = $meta->LoadArray( 'occurrence_id = %d', array( $occurence->id ) );
134
- }
135
- return $this->_meta;
136
  }
137
 
138
  /**
139
- * Loads a meta item given its name.
140
- *
141
- * @param object $occurence - Occurrence model instance.
142
- * @param string $name - Meta name.
143
- * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
144
- * @return WSAL_Models_Meta The meta item, be sure to checked if it was loaded successfully.
145
  */
146
- public function GetNamedMeta( $occurence, $name ) {
147
  $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
148
 
149
- return $meta->LoadByNameAndOccurrenceId( $name, $occurence->id );
150
  }
151
 
152
  /**
153
- * Returns the first meta value from a given set of names.
154
- * Useful when you have a mix of items that could provide
155
- * a particular detail.
156
- *
157
- * @param object $occurrence - Occurrence model instance.
158
- * @param array $names - List of meta names.
159
- * @return WSAL_Models_Meta The first meta item that exists.
160
  */
161
  public function GetFirstNamedMeta( $occurrence, $names ) {
162
  $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
@@ -164,123 +204,59 @@ class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord im
164
  $query = 'occurrence_id = %d AND ' . $query . ' ORDER BY name DESC LIMIT 1';
165
  array_unshift( $names, $occurrence->id ); // Prepend args with occurrence id.
166
 
167
- $this->_meta = $meta->Load( $query, $names );
168
- return $meta->getModel()->LoadData( $this->_meta );
169
-
170
- // TODO: Do we want to reintroduce is loaded check/logic?
171
- // return $meta->IsLoaded() ? $meta : null;
172
  }
173
 
174
  /**
175
- * Gets occurrences of the same type by IP and Username within specified time frame.
176
- *
177
- * @param array $args - User arguments.
178
- * @return WSAL_Models_Occurrence[]
179
  */
180
  public function CheckKnownUsers( $args = array() ) {
181
- $tt2 = new WSAL_Adapters_MySQL_Meta( $this->connection );
182
  return $this->LoadMultiQuery(
183
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
184
- INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
185
- and ipMeta.name = "ClientIP"
186
- and ipMeta.value = %s
187
- INNER JOIN `' . $tt2->GetTable() . '` usernameMeta on usernameMeta.occurrence_id = occurrence.id
188
- and usernameMeta.name = "Username"
189
- and usernameMeta.value = %s
190
- WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
191
- AND (created_on BETWEEN %d AND %d)
192
- GROUP BY occurrence.id',
193
  $args
194
  );
195
  }
196
 
197
  /**
198
- * Gets occurrences of the same type by IP within specified time frame.
199
- *
200
- * @param array $args - User arguments.
201
- * @return WSAL_Models_Occurrence[]
202
  */
203
- public function CheckUnKnownUsers( $args = array() ) {
204
- $tt2 = new WSAL_Adapters_MySQL_Meta( $this->connection );
205
  return $this->LoadMultiQuery(
206
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
207
- INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
208
- and ipMeta.name = "ClientIP" and ipMeta.value = %s
209
- WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
210
- AND (created_on BETWEEN %d AND %d)
211
- GROUP BY occurrence.id',
212
  $args
213
  );
214
  }
215
 
216
  /**
217
- * Gets occurrences of the alert 1003.
218
- *
219
- * @param array $args - User arguments.
220
- * @return WSAL_Models_Occurrence[]
221
  */
222
  public function check_alert_1003( $args = array() ) {
223
  return $this->LoadMultiQuery(
224
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
225
- WHERE (occurrence.alert_id = %d)
226
- AND (occurrence.site_id = %d)
227
- AND (occurrence.created_on BETWEEN %d AND %d)
228
- GROUP BY occurrence.id',
229
  $args
230
  );
231
  }
232
 
233
  /**
234
- * Add conditions to the Query
235
- *
236
- * @param string $query - Query.
237
- *
238
- * @return string[]
239
- */
240
- protected function prepareOccurrenceQuery( $query ) {
241
- $search_query_parameters = array();
242
- $search_conditions = array();
243
- $conditions = $query->getConditions();
244
-
245
- // BUG: not all conditions are occurence related. maybe it's just a field site_id. need seperate arrays.
246
- if ( ! empty( $conditions ) ) {
247
- $tmp = new WSAL_Adapters_MySQL_Meta( $this->connection );
248
- $s_where_clause = '';
249
- foreach ( $conditions as $field => $value ) {
250
- if ( ! empty( $s_where_clause ) ) {
251
- $s_where_clause .= ' AND ';
252
- }
253
- $s_where_clause .= 'name = %s AND value = %s';
254
- $search_query_parameters[] = $field;
255
- $search_query_parameters[] = $value;
256
- }
257
-
258
- $search_conditions[] = 'id IN (
259
- SELECT DISTINCT occurrence_id
260
- FROM ' . $tmp->GetTable() . '
261
- WHERE ' . $s_where_clause . '
262
- )';
263
- }
264
-
265
- // Do something with search query parameters and search conditions - give them to the query adapter?
266
- return $search_conditions;
267
- }
268
-
269
- /**
270
- * Gets occurrence by Post_id.
271
- *
272
- * @param int $post_id - Post ID.
273
- * @return WSAL_Models_Occurrence[]
274
  */
275
  public function GetByPostID( $post_id ) {
276
- $tt2 = new WSAL_Adapters_MySQL_Meta( $this->connection );
277
  return $this->LoadMultiQuery(
278
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '`AS occurrence
279
- INNER JOIN `' . $tt2->GetTable() . '`AS postMeta ON postMeta.occurrence_id = occurrence.id
280
- and postMeta.name = "PostID"
281
- and postMeta.value = %d
282
- GROUP BY occurrence.id
283
- ORDER BY created_on DESC',
284
  array( $post_id )
285
  );
286
  }
@@ -289,6 +265,7 @@ class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord im
289
  * Create relevant indexes on the occurrence table.
290
  */
291
  public function create_indexes() {
 
292
  $db_connection = $this->get_connection();
293
  // check if an index exists.
294
  if ( $db_connection->query( 'SELECT COUNT(1) IndexIsThere FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() AND table_name="' . $this->GetTable() . '" AND index_name="created_on"' ) ) {
@@ -300,4 +277,100 @@ class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord im
300
  $db_connection->query( 'CREATE INDEX created_on ON ' . $this->GetTable() . ' (created_on)' );
301
  }
302
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  }
22
  class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_OccurrenceInterface {
23
 
24
  /**
25
+ * @inheritDoc
 
 
26
  */
27
  protected $_table = 'wsal_occurrences';
28
 
33
  */
34
  protected $_idkey = 'id';
35
 
 
 
 
 
 
 
 
36
  /**
37
  * Occurrence id.
38
  *
62
  public $created_on = 0.0;
63
 
64
  /**
65
+ * Client IP address.
66
+ *
67
+ * @var string
68
+ * @since 4.4.0
69
+ */
70
+ public $client_ip = '';
71
+
72
+ /**
73
+ * Severity.
74
+ *
75
+ * @var int
76
+ * @since 4.4.0
77
+ */
78
+ public $severity = '';
79
+
80
+ /**
81
+ * Event object.
82
+ *
83
+ * @var string
84
+ * @since 4.4.0
85
+ */
86
+ public $object = '';
87
+
88
+ /**
89
+ * Event type.
90
+ *
91
+ * @var string
92
+ * @since 4.4.0
93
+ */
94
+ public $event_type = '';
95
+
96
+ /**
97
+ * User agent string.
98
+ *
99
+ * @var string
100
+ * @since 4.4.0
101
+ */
102
+ public $user_agent = '';
103
+
104
+ /**
105
+ * Comma separated user roles of the user belonging to the event.
106
  *
107
+ * @var string
108
+ * @since 4.4.0
109
+ */
110
+ public $user_roles = '';
111
+
112
+ /**
113
+ * Username of the user belonging to the event.
114
+ *
115
+ * @var string
116
+ * @since 4.4.0
117
+ */
118
+ public $username = null;
119
+
120
+ /**
121
+ * User ID of the user belonging to the event.
122
+ *
123
+ * @var int
124
+ * @since 4.4.0
125
  */
126
+ public $user_id = null;
127
 
128
  /**
129
+ * Session ID.
130
  *
131
+ * @var string
132
+ * @since 4.4.0
133
  */
134
+ public $session_id = '';
135
 
136
  /**
137
+ * Post status.
138
  *
139
+ * @var string
140
+ * @since 4.4.0
141
+ */
142
+ public $post_status = '';
143
+
144
+ /**
145
+ * Post status.
146
+ *
147
+ * @var string
148
+ * @since 4.4.0
149
+ */
150
+ public $post_type = '';
151
+
152
+ /**
153
+ * Post ID.
154
+ *
155
+ * @var int
156
+ * @since 4.4.0
157
+ */
158
+ public $post_id = 0;
159
+
160
+ /**
161
+ * @inheritDoc
162
  */
163
  protected function GetTableOptions() {
164
  return parent::GetTableOptions() . ',' . PHP_EOL
165
+ . ' KEY site_alert_created (site_id,alert_id,created_on)';
166
  }
167
 
168
  /**
169
+ * @inheritDoc
170
  *
171
  * @return WSAL_Models_Occurrence
172
  */
173
  public function GetModel() {
174
+ $result = new WSAL_Models_Occurrence();
175
+ $result->setAdapter( $this );
176
 
177
+ return $result;
 
 
 
 
 
 
 
 
 
 
 
 
178
  }
179
 
180
  /**
181
+ * @inheritDoc
182
+ */
183
+ public function GetMultiMeta( $occurrence ) {
184
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
185
+
186
+ return $meta->LoadArray( 'occurrence_id = %d', array( $occurrence->id ) );
 
 
 
 
 
 
187
  }
188
 
189
  /**
190
+ * @inheritDoc
 
 
 
 
 
191
  */
192
+ public function GetNamedMeta( $occurrence, $name ) {
193
  $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
194
 
195
+ return $meta->LoadByNameAndOccurrenceId( $name, $occurrence->id );
196
  }
197
 
198
  /**
199
+ * @inheritDoc
 
 
 
 
 
 
200
  */
201
  public function GetFirstNamedMeta( $occurrence, $names ) {
202
  $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
204
  $query = 'occurrence_id = %d AND ' . $query . ' ORDER BY name DESC LIMIT 1';
205
  array_unshift( $names, $occurrence->id ); // Prepend args with occurrence id.
206
 
207
+ return $meta->getModel()->LoadData( $meta->Load( $query, $names ) );
 
 
 
 
208
  }
209
 
210
  /**
211
+ * @inheritDoc
 
 
 
212
  */
213
  public function CheckKnownUsers( $args = array() ) {
 
214
  return $this->LoadMultiQuery(
215
+ "SELECT * FROM `{$this->GetTable()}` "
216
+ . " WHERE client_ip = %s "
217
+ . " AND username = %s "
218
+ . " AND alert_id = %d "
219
+ . " AND site_id = %d "
220
+ . " AND ( created_on BETWEEN %d AND %d );",
 
 
 
 
221
  $args
222
  );
223
  }
224
 
225
  /**
226
+ * @inheritDoc
 
 
 
227
  */
228
+ public function CheckUnknownUsers( $args = array() ) {
 
229
  return $this->LoadMultiQuery(
230
+ "SELECT * FROM `{$this->GetTable()}` "
231
+ . " WHERE client_ip = %s "
232
+ . " AND alert_id = %d "
233
+ . " AND site_id = %d "
234
+ . " AND ( created_on BETWEEN %d AND %d );",
 
235
  $args
236
  );
237
  }
238
 
239
  /**
240
+ * @inheritDoc
 
 
 
241
  */
242
  public function check_alert_1003( $args = array() ) {
243
  return $this->LoadMultiQuery(
244
+ 'SELECT * FROM `' . $this->GetTable() . '`
245
+ WHERE (alert_id = %d)
246
+ AND (site_id = %d)
247
+ AND (created_on BETWEEN %d AND %d);',
 
248
  $args
249
  );
250
  }
251
 
252
  /**
253
+ * @inheritDoc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  */
255
  public function GetByPostID( $post_id ) {
 
256
  return $this->LoadMultiQuery(
257
+ "SELECT occurrence.* FROM `{$this->GetTable()}` "
258
+ . " WHERE post_id = %d "
259
+ . " ORDER BY created_on DESC;",
 
 
 
260
  array( $post_id )
261
  );
262
  }
265
  * Create relevant indexes on the occurrence table.
266
  */
267
  public function create_indexes() {
268
+ $index_exists = false;
269
  $db_connection = $this->get_connection();
270
  // check if an index exists.
271
  if ( $db_connection->query( 'SELECT COUNT(1) IndexIsThere FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() AND table_name="' . $this->GetTable() . '" AND index_name="created_on"' ) ) {
277
  $db_connection->query( 'CREATE INDEX created_on ON ' . $this->GetTable() . ' (created_on)' );
278
  }
279
  }
280
+
281
+ /**
282
+ * @inheritDoc
283
+ */
284
+ public function get_all_with_meta_to_migrate( $limit ) {
285
+ $meta_adapter = new WSAL_Adapters_MySQL_Meta( $this->connection );
286
+
287
+ $meta_keys = array_map( function ( $value ) {
288
+ return '"' . $value . '"';
289
+ }, array_keys( WSAL_Models_Occurrence::$migrated_meta ) );
290
+
291
+ return $this->LoadMultiQuery(
292
+ "SELECT o.* FROM `{$this->GetTable()}` o "
293
+ . " INNER JOIN `{$meta_adapter->GetTable()}` m "
294
+ . " ON m.occurrence_id = o.id "
295
+ . " WHERE m.name IN (" . implode( ',', $meta_keys ) . ") "
296
+ . " GROUP BY o.id "
297
+ . " ORDER BY created_on DESC "
298
+ . " LIMIT 0, %d;",
299
+ array( $limit )
300
+ );
301
+ }
302
+
303
+ /**
304
+ * Get distinct values of IPs.
305
+ *
306
+ * @param int $limit - (Optional) Limit.
307
+ *
308
+ * @return array - Distinct values of IPs.
309
+ */
310
+ public function GetMatchingIPs( $limit = null ) {
311
+ $_wpdb = $this->connection;
312
+ $sql = "SELECT DISTINCT client_ip FROM {$this->GetTable()}";
313
+ if ( ! is_null( $limit ) ) {
314
+ $sql .= ' LIMIT ' . $limit;
315
+ }
316
+ $ips = $_wpdb->get_col( $sql );
317
+ $result = array();
318
+ foreach ( $ips as $ip ) {
319
+ if ( 0 === strlen( trim( $ip ) ) ) {
320
+ continue;
321
+ }
322
+ array_push( $result, $ip );
323
+ }
324
+
325
+ return array_unique( $result );
326
+ }
327
+
328
+ /**
329
+ * @inheritDoc
330
+ *
331
+ * username and user_id columns have to be added manually because function get_object_vars doesn't return
332
+ * uninitialised properties. These two cannot have the default value set because some database queries rely on
333
+ * having null values in the database.
334
+ *
335
+ * @since 4.4.0
336
+ */
337
+ public function GetColumns() {
338
+ if ( ! empty( $this->_column_cache ) ) {
339
+ return $this->_column_cache;
340
+ }
341
+
342
+ $result = parent::GetColumns();
343
+ foreach ( [ 'username', 'user_id' ] as $extra_column ) {
344
+ if ( ! in_array( $extra_column, $result ) ) {
345
+ array_push( $result, $extra_column );
346
+ }
347
+ }
348
+
349
+ $this->_column_cache = $result;
350
+
351
+ return $result;
352
+ }
353
+
354
+ /**
355
+ * @inheritDoc
356
+ *
357
+ * @param WSAL_Models_Occurrence $copy
358
+ *
359
+ * @since 4.4.0
360
+ */
361
+ protected function _GetSqlColumnDefinition( $copy, $key ) {
362
+ if ( 'username' === $key ) {
363
+ return " username VARCHAR(255) NULL, ";
364
+ }
365
+
366
+ if ( 'user_id' === $key ) {
367
+ return " user_id BIGINT NULL, ";
368
+ }
369
+
370
+ if ( is_string( $copy->$key ) ) {
371
+ return $key . ' VARCHAR(255) NOT NULL,' . PHP_EOL;
372
+ }
373
+
374
+ return parent::_GetSqlColumnDefinition( $copy, $key );
375
+ }
376
  }
classes/Adapters/MySQL/QueryAdapter.php CHANGED
@@ -19,6 +19,7 @@ if ( ! defined( 'ABSPATH' ) ) {
19
  * the arguments.
20
  *
21
  * @package wsal
 
22
  */
23
  class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
24
 
@@ -32,10 +33,10 @@ class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
32
  /**
33
  * Method: Constructor.
34
  *
35
- * @param array $conn - Connection array.
36
  */
37
- public function __construct( $conn ) {
38
- $this->connection = $conn;
39
  }
40
 
41
  /**
@@ -132,16 +133,16 @@ class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
132
  }
133
 
134
  /**
135
- * Execute query and return data as $ar_cls objects.
136
- *
137
- * @param object $query - Query object.
138
- *
139
- * @return WSAL_Models_ActiveRecord[]
140
  */
141
  public function Execute( $query ) {
142
  $args = array();
143
  $sql = $this->GetSql( $query, $args );
144
 
 
 
 
 
145
  $occurrence_adapter = $query->getConnector()->getAdapter( 'Occurrence' );
146
 
147
  if ( in_array( $occurrence_adapter->GetTable(), $query->getFrom() ) ) {
@@ -152,10 +153,7 @@ class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
152
  }
153
 
154
  /**
155
- * Count query
156
- *
157
- * @param object $query - Query object.
158
- * @return integer counting records.
159
  */
160
  public function Count( $query ) {
161
  // Back up columns, use COUNT as default column and generate sql.
@@ -164,7 +162,7 @@ class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
164
  $query->addColumn( 'COUNT(*)' );
165
 
166
  $args = array();
167
- $sql = $this->GetSql( $query, $args );
168
 
169
  // Restore columns.
170
  $query->setColumns( $cols );
@@ -185,9 +183,7 @@ class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
185
  }
186
 
187
  /**
188
- * Query for deleting records
189
- *
190
- * @param object $query query object.
191
  */
192
  public function Delete( $query ) {
193
  $result = $this->GetSqlDelete( $query );
@@ -296,6 +292,9 @@ class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
296
  return $search_conditions;
297
  }
298
 
 
 
 
299
  public function IsConnected() {
300
  return ( $this->connection && $this->connection->has_connected );
301
  }
19
  * the arguments.
20
  *
21
  * @package wsal
22
+ * @subpackage adapters
23
  */
24
  class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
25
 
33
  /**
34
  * Method: Constructor.
35
  *
36
+ * @param array $connection - Connection array.
37
  */
38
+ public function __construct( $connection ) {
39
+ $this->connection = $connection;
40
  }
41
 
42
  /**
133
  }
134
 
135
  /**
136
+ * @inheritDoc
 
 
 
 
137
  */
138
  public function Execute( $query ) {
139
  $args = array();
140
  $sql = $this->GetSql( $query, $args );
141
 
142
+ $args = array_filter( $args, function ( $item ) {
143
+ return ( '' !== $item );
144
+ } );
145
+
146
  $occurrence_adapter = $query->getConnector()->getAdapter( 'Occurrence' );
147
 
148
  if ( in_array( $occurrence_adapter->GetTable(), $query->getFrom() ) ) {
153
  }
154
 
155
  /**
156
+ * @inheritDoc
 
 
 
157
  */
158
  public function Count( $query ) {
159
  // Back up columns, use COUNT as default column and generate sql.
162
  $query->addColumn( 'COUNT(*)' );
163
 
164
  $args = array();
165
+ $sql = $this->GetSql( $query, $args );
166
 
167
  // Restore columns.
168
  $query->setColumns( $cols );
183
  }
184
 
185
  /**
186
+ * @inheritDoc
 
 
187
  */
188
  public function Delete( $query ) {
189
  $result = $this->GetSqlDelete( $query );
292
  return $search_conditions;
293
  }
294
 
295
+ /**
296
+ * @inheritDoc
297
+ */
298
  public function IsConnected() {
299
  return ( $this->connection && $this->connection->has_connected );
300
  }
classes/Adapters/MySQL/TmpUserAdapter.php CHANGED
@@ -32,7 +32,7 @@ class WSAL_Adapters_MySQL_TmpUser extends WSAL_Adapters_MySQL_ActiveRecord {
32
  protected $_table = 'wsal_tmp_users';
33
 
34
  /**
35
- * Returns the model class for adapter.
36
  *
37
  * @return WSAL_Models_TmpUser
38
  */
@@ -41,22 +41,17 @@ class WSAL_Adapters_MySQL_TmpUser extends WSAL_Adapters_MySQL_ActiveRecord {
41
  }
42
 
43
  /**
44
- * Must return SQL for creating table.
45
- *
46
- * @param mixed $prefix - Prefix.
47
- * @return string
48
  */
49
  protected function _GetInstallQuery( $prefix = false ) {
50
  $_wpdb = $this->connection;
51
  $table_name = ( $prefix ) ? $this->GetWPTable() : $this->GetTable();
52
  $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
53
- $sql .= 'ID BIGINT NOT NULL,' . PHP_EOL;
54
- $sql .= 'user_login VARCHAR(60) NOT NULL,' . PHP_EOL;
55
- $sql .= 'INDEX (ID)' . PHP_EOL;
56
- $sql .= ')';
57
- if ( ! empty( $_wpdb->charset ) ) {
58
- $sql .= ' DEFAULT CHARACTER SET ' . $_wpdb->charset;
59
- }
60
  return $sql;
61
  }
62
  }
32
  protected $_table = 'wsal_tmp_users';
33
 
34
  /**
35
+ * @inheritDoc
36
  *
37
  * @return WSAL_Models_TmpUser
38
  */
41
  }
42
 
43
  /**
44
+ * @inheritDoc
 
 
 
45
  */
46
  protected function _GetInstallQuery( $prefix = false ) {
47
  $_wpdb = $this->connection;
48
  $table_name = ( $prefix ) ? $this->GetWPTable() : $this->GetTable();
49
  $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
50
+ $sql .= 'ID BIGINT NOT NULL,' . PHP_EOL;
51
+ $sql .= 'user_login VARCHAR(60) NOT NULL,' . PHP_EOL;
52
+ $sql .= 'INDEX (ID)' . PHP_EOL;
53
+ $sql .= ') ' . $_wpdb->get_charset_collate();
54
+
 
 
55
  return $sql;
56
  }
57
  }
classes/Adapters/OccurrenceInterface.php CHANGED
@@ -20,41 +20,82 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  interface WSAL_Adapters_OccurrenceInterface {
21
 
22
  /**
23
- * Get Meta.
24
  *
25
- * @param object $occurence - Instance of occurence object.
 
 
 
26
  */
27
- public function GetMeta( $occurence );
28
 
29
  /**
30
  * Loads a meta item given its name.
31
  *
32
- * @param object $occurence - Instance of occurence object.
33
  * @param string $name - Meta name.
 
 
 
34
  */
35
- public function GetNamedMeta( $occurence, $name );
36
 
37
  /**
38
  * Returns the first meta value from a given set of names.
39
  * Useful when you have a mix of items that could provide
40
  * a particular detail.
41
  *
42
- * @param object $occurrence - Instance of occurrence object.
43
- * @param array $names - List of Meta names.
 
 
44
  */
45
  public function GetFirstNamedMeta( $occurrence, $names );
46
 
47
  /**
48
  * Gets occurrences of the same type by IP and Username within specified time frame.
49
  *
50
- * @param array $args - Arguments.
 
 
51
  */
52
  public function CheckKnownUsers( $args = array() );
53
 
54
  /**
55
  * Gets occurrences of the same type by IP within specified time frame.
56
  *
57
- * @param array $args - Arguments.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  */
59
- public function CheckUnKnownUsers( $args = array() );
60
  }
20
  interface WSAL_Adapters_OccurrenceInterface {
21
 
22
  /**
23
+ * Returns all metadata related to this event.
24
  *
25
+ * @param WSAL_Models_Occurrence $occurrence - Occurrence model instance.
26
+ *
27
+ * @return WSAL_Models_Meta[]
28
+ * @see WSAL_Adapters_MySQL_ActiveRecord::LoadArray()
29
  */
30
+ public function GetMultiMeta( $occurrence );
31
 
32
  /**
33
  * Loads a meta item given its name.
34
  *
35
+ * @param object $occurrence - Occurrence model instance.
36
  * @param string $name - Meta name.
37
+ *
38
+ * @return WSAL_Models_Meta The meta item, be sure to check if it was loaded successfully.
39
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
40
  */
41
+ public function GetNamedMeta( $occurrence, $name );
42
 
43
  /**
44
  * Returns the first meta value from a given set of names.
45
  * Useful when you have a mix of items that could provide
46
  * a particular detail.
47
  *
48
+ * @param object $occurrence - Occurrence model instance.
49
+ * @param array $names - List of meta names.
50
+ *
51
+ * @return WSAL_Models_Meta The first meta item that exists.
52
  */
53
  public function GetFirstNamedMeta( $occurrence, $names );
54
 
55
  /**
56
  * Gets occurrences of the same type by IP and Username within specified time frame.
57
  *
58
+ * @param array $args - User arguments.
59
+ *
60
+ * @return WSAL_Models_Occurrence[]
61
  */
62
  public function CheckKnownUsers( $args = array() );
63
 
64
  /**
65
  * Gets occurrences of the same type by IP within specified time frame.
66
  *
67
+ * @param array $args - User arguments.
68
+ *
69
+ * @return WSAL_Models_Occurrence[]
70
+ */
71
+ public function CheckUnknownUsers( $args = array() );
72
+
73
+ /**
74
+ * Gets occurrence by Post_id.
75
+ *
76
+ * @param int $post_id - Post ID.
77
+ *
78
+ * @return WSAL_Models_Occurrence[]
79
+ */
80
+ public function GetByPostID( $post_id );
81
+
82
+ /**
83
+ * Gets occurrences of the alert 1003.
84
+ *
85
+ * @param array $args - User arguments.
86
+ *
87
+ * @return WSAL_Models_Occurrence[]
88
+ */
89
+ public function check_alert_1003( $args = array() );
90
+
91
+ /**
92
+ * Retrieves occurrences that have metadata that needs to be migrated to the occurrences table. This relates to the
93
+ * database schema change done in version 4.4.0.
94
+ *
95
+ * @param int $limit
96
+ *
97
+ * @return WSAL_Models_Occurrence[]
98
+ * @since 4.4.0
99
  */
100
+ public function get_all_with_meta_to_migrate( $limit );
101
  }
classes/Adapters/QueryInterface.php CHANGED
@@ -42,6 +42,7 @@ interface WSAL_Adapters_QueryInterface {
42
 
43
  /**
44
  * Checks if the adapter is successfully connected.
 
45
  * @return bool True if the adapter is connected. False otherwise.
46
  * @since 4.3.2
47
  */
42
 
43
  /**
44
  * Checks if the adapter is successfully connected.
45
+ *
46
  * @return bool True if the adapter is connected. False otherwise.
47
  * @since 4.3.2
48
  */
classes/Alert.php CHANGED
@@ -182,7 +182,7 @@ final class WSAL_Alert {
182
 
183
  $end_of_line = $formatter->get_end_of_line();
184
 
185
- // process metadata and links introduced as part of alert definition in version 4.2.1
186
  if ( $formatter->supports_metadata() ) {
187
  $metadata_result = $this->get_formatted_metadata( $formatter, $meta_data, $occurrence_id );
188
  if ( ! empty( $metadata_result ) ) {
@@ -210,13 +210,13 @@ final class WSAL_Alert {
210
  * Retrieves a value for a particular meta variable expression.
211
  *
212
  * @param string $expr Expression, eg: User->Name looks for a Name property for meta named User.
213
- * @param array $meta_data (Optional) Meta data relevant to expression.
214
  *
215
  * @return mixed The value nearest to the expression.
216
  */
217
  protected function GetMetaExprValue( $expr, $meta_data = array() ) {
218
  $expr = preg_replace( '/%/', '', $expr );
219
- if ( 'IPAddress' == $expr ) {
220
  if ( array_key_exists( 'IPAddress', $meta_data ) ) {
221
  return implode( ', ', $meta_data['IPAddress'] );
222
  }
@@ -232,16 +232,18 @@ final class WSAL_Alert {
232
  if ( is_scalar( $meta ) || is_null( $meta ) ) {
233
  return $meta; // This isn't 100% correct.
234
  }
235
- $meta = is_array( $meta ) ? $meta[ $part ] : ( isset( $meta->$part ) ? $meta->$part : 'NULL' );
236
  }
237
 
238
  return is_scalar( $meta ) ? (string) $meta : var_export( $meta, true );
239
  }
240
 
241
  /**
242
- * @param WSAL_AlertFormatter $formatter
243
- * @param array $meta_data
244
- * @param int $occurrence_id
 
 
245
  *
246
  * @return string
247
  * @throws Freemius_Exception
@@ -252,7 +254,7 @@ final class WSAL_Alert {
252
  $metadata_as_array = $this->get_metadata_as_array( $formatter, $meta_data, $occurrence_id );
253
  if ( ! empty( $metadata_as_array ) ) {
254
 
255
- $meta_result_parts = [];
256
  foreach ( $metadata_as_array as $meta_label => $meta_expression ) {
257
  if ( ! empty( $meta_expression ) ) {
258
  array_push( $meta_result_parts, $meta_label . ': ' . $formatter->wrap_in_hightlight_markup( $meta_expression ) );
@@ -267,25 +269,28 @@ final class WSAL_Alert {
267
  }
268
 
269
  /**
270
- * @param WSAL_AlertFormatter $formatter
271
- * @param array $meta_data
272
- * @param int $occurrence_id
 
 
273
  *
274
  * @return array
 
275
  * @since 4.2.1
276
  */
277
  public function get_metadata_as_array( $formatter, $meta_data, $occurrence_id ) {
278
- $result = [];
279
  if ( ! empty( $this->metadata ) ) {
280
  foreach ( $this->metadata as $meta_label => $meta_token ) {
281
  if ( strlen( $meta_token ) === 0 ) {
282
  continue;
283
  }
284
 
285
- // pure alert meta lookup based on meta token
286
  $meta_expression = $this->GetMetaExprValue( $meta_token, $meta_data );
287
 
288
- // additional alert meta processing - handles derived or decorated alert data
289
  $meta_expression = $formatter->format_meta_expression( $meta_token, $meta_expression, $occurrence_id );
290
 
291
  if ( ! empty( $meta_expression ) ) {
182
 
183
  $end_of_line = $formatter->get_end_of_line();
184
 
185
+ // Process metadata and links introduced as part of alert definition in version 4.2.1.
186
  if ( $formatter->supports_metadata() ) {
187
  $metadata_result = $this->get_formatted_metadata( $formatter, $meta_data, $occurrence_id );
188
  if ( ! empty( $metadata_result ) ) {
210
  * Retrieves a value for a particular meta variable expression.
211
  *
212
  * @param string $expr Expression, eg: User->Name looks for a Name property for meta named User.
213
+ * @param array $meta_data (Optional) Meta data relevant to expression.
214
  *
215
  * @return mixed The value nearest to the expression.
216
  */
217
  protected function GetMetaExprValue( $expr, $meta_data = array() ) {
218
  $expr = preg_replace( '/%/', '', $expr );
219
+ if ( 'IPAddress' === $expr ) {
220
  if ( array_key_exists( 'IPAddress', $meta_data ) ) {
221
  return implode( ', ', $meta_data['IPAddress'] );
222
  }
232
  if ( is_scalar( $meta ) || is_null( $meta ) ) {
233
  return $meta; // This isn't 100% correct.
234
  }
235
+ $meta = is_array( $meta ) && array_key_exists( $part, $meta ) ? $meta[ $part ] : ( isset( $meta->$part ) ? $meta->$part : 'NULL' );
236
  }
237
 
238
  return is_scalar( $meta ) ? (string) $meta : var_export( $meta, true );
239
  }
240
 
241
  /**
242
+ * Retrieves formatted meta data item (label and data).
243
+ *
244
+ * @param WSAL_AlertFormatter $formatter Alert formatter.
245
+ * @param array $meta_data Meta data.
246
+ * @param int $occurrence_id Occurrence ID.
247
  *
248
  * @return string
249
  * @throws Freemius_Exception
254
  $metadata_as_array = $this->get_metadata_as_array( $formatter, $meta_data, $occurrence_id );
255
  if ( ! empty( $metadata_as_array ) ) {
256
 
257
+ $meta_result_parts = array();
258
  foreach ( $metadata_as_array as $meta_label => $meta_expression ) {
259
  if ( ! empty( $meta_expression ) ) {
260
  array_push( $meta_result_parts, $meta_label . ': ' . $formatter->wrap_in_hightlight_markup( $meta_expression ) );
269
  }
270
 
271
  /**
272
+ * Retrieves metadata as an associative array.
273
+ *
274
+ * @param WSAL_AlertFormatter $formatter Alert formatter.
275
+ * @param array $meta_data Meta data.
276
+ * @param int $occurrence_id Occurrence ID.
277
  *
278
  * @return array
279
+ * @throws Freemius_Exception
280
  * @since 4.2.1
281
  */
282
  public function get_metadata_as_array( $formatter, $meta_data, $occurrence_id ) {
283
+ $result = array();
284
  if ( ! empty( $this->metadata ) ) {
285
  foreach ( $this->metadata as $meta_label => $meta_token ) {
286
  if ( strlen( $meta_token ) === 0 ) {
287
  continue;
288
  }
289
 
290
+ // Pure alert meta lookup based on meta token.
291
  $meta_expression = $this->GetMetaExprValue( $meta_token, $meta_data );
292
 
293
+ // Additional alert meta processing - handles derived or decorated alert data.
294
  $meta_expression = $formatter->format_meta_expression( $meta_token, $meta_expression, $occurrence_id );
295
 
296
  if ( ! empty( $meta_expression ) ) {
classes/AlertFormatter.php CHANGED
@@ -72,6 +72,18 @@ final class WSAL_AlertFormatter {
72
 
73
  return '';
74
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  case in_array( $expression, array( '%MetaValue%', '%MetaValueOld%', '%MetaValueNew%' ) ):
76
  // trim the meta value to the maximum length and append configured ellipses sequence
77
  $result = mb_strlen( $value ) > $this->configuration->getMaxMetaValueLength() ? ( mb_substr( $value, 0, 50 ) . $this->configuration->getEllipsesSequence() ) : $value;
72
 
73
  return '';
74
 
75
+ case in_array( $expression, array( '%path%', '%old_path%', '%FilePath%' ) ):
76
+ // concatenate directory and file paths
77
+ $max_length = 50;
78
+ if ( $this->configuration->isJsInLinksAllowed() && strlen( $value ) > $max_length ) {
79
+ $result = '<span>' . substr( $value, 0, $max_length ) . '</span>';
80
+ $result .= "<a href=\"#\" data-shortened-text='{$value}'>" . $this->configuration->getEllipsesSequence() . "</a>";
81
+
82
+ return $result;
83
+ }
84
+
85
+ return $value;
86
+
87
  case in_array( $expression, array( '%MetaValue%', '%MetaValueOld%', '%MetaValueNew%' ) ):
88
  // trim the meta value to the maximum length and append configured ellipses sequence
89
  $result = mb_strlen( $value ) > $this->configuration->getMaxMetaValueLength() ? ( mb_substr( $value, 0, 50 ) . $this->configuration->getEllipsesSequence() ) : $value;
classes/AlertManager.php CHANGED
@@ -83,15 +83,19 @@ final class WSAL_AlertManager {
83
  public $ignored_cpts = array();
84
 
85
  /**
 
 
86
  * @var string Date format.
87
  */
88
  private $date_format;
89
 
90
- /**
91
- * @var string Sanitized date format.
92
- * @since 4.2.1
93
- */
94
- private $sanitized_date_format;
 
 
95
 
96
  /**
97
  * Create new AlertManager instance.
@@ -100,7 +104,7 @@ final class WSAL_AlertManager {
100
  */
101
  public function __construct( WpSecurityAuditLog $plugin ) {
102
  $this->plugin = $plugin;
103
- foreach ( glob( dirname( __FILE__ ) . '/Loggers/*.php' ) as $file ) {
104
  $this->AddFromFile( $file );
105
  }
106
 
@@ -233,7 +237,8 @@ final class WSAL_AlertManager {
233
 
234
  // If user or user role is enable then go ahead.
235
  if ( $this->CheckEnableUserRoles( $username, $roles ) ) {
236
- $data['Timestamp'] = current_time( 'U.u', 'true' );
 
237
  if ( $delayed ) {
238
  $this->TriggerIf( $type, $data, null );
239
  } else {
@@ -330,15 +335,13 @@ final class WSAL_AlertManager {
330
  /**
331
  * Method: Commit an alert now.
332
  *
333
- * @param int $type - Alert type.
334
- * @param array $data - Data of the alert.
335
- * @param array $cond - Condition for the alert.
336
- * @param bool $_retry - Retry.
337
  *
338
  * @return mixed
339
- * @throws Exception - Error if alert is not registered.
340
  * @internal
341
- *
342
  */
343
  protected function _CommitItem( $type, $data, $cond, $_retry = true ) {
344
  // Double NOT operation here is intentional. Same as ! ( bool ) [ $value ]
@@ -356,7 +359,8 @@ final class WSAL_AlertManager {
356
  } else {
357
  // In general this shouldn't happen, but it could, so we handle it here.
358
  /* translators: Event ID */
359
- throw new Exception( sprintf( esc_html__( 'Event with code %d has not be registered.', 'wp-security-audit-log' ), $type ) );
 
360
  }
361
  }
362
  }
@@ -407,40 +411,41 @@ final class WSAL_AlertManager {
407
  /**
408
  * Register an alert type.
409
  *
410
- * @param string $category Category name.
411
  * @param string $subcategory Subcategory name.
412
- * @param array $info Event information from defaults.php.
413
- *
414
- * @throws Exception - Error if alert is already registered.
415
  */
416
  public function Register( $category, $subcategory, $info ) {
417
 
418
- // default for optional fields
419
- $metadata = [];
420
- $links = [];
421
  $object = '';
422
  $event_type = '';
423
 
424
  $definition_items_count = count( $info );
425
- if ( 8 == $definition_items_count ) {
426
- // most recent event definition introduced in version 4.2.1
427
  list( $code, $severity, $desc, $message, $metadata, $links, $object, $event_type ) = $info;
428
- } else if (6 == $definition_items_count ) {
429
- // legacy event definition for backwards compatibility (used prior to version 4.2.1)
430
  list( $code, $severity, $desc, $message, $object, $event_type ) = $info;
431
  } else {
432
- // even older legacy event definition for backwards compatibility
433
  list( $code, $severity, $desc, $message ) = $info;
434
  }
435
 
436
  if ( is_string( $links ) ) {
437
- $links = [ $links ];
438
  }
439
-
440
  if ( isset( $this->_alerts[ $code ] ) ) {
441
  add_action( 'admin_notices', array( $this, 'duplicate_event_notice' ) );
442
  /* Translators: Event ID */
443
- throw new Exception( sprintf( esc_html__( 'Event %s already registered with WP Activity Log.', 'wp-security-audit-log' ), $code ) );
 
 
 
444
  }
445
 
446
  /**
@@ -453,7 +458,6 @@ final class WSAL_AlertManager {
453
  * @param integer $code - Event ID.
454
  *
455
  * @since 4.3.2
456
- *
457
  */
458
  $metadata = apply_filters( 'wsal_event_metadata_definition', $metadata, $code );
459
 
@@ -465,8 +469,6 @@ final class WSAL_AlertManager {
465
  *
466
  * @param array $groups - An array with group name as the index and an array of group items as the value.
467
  * Item values is an array of [type, code, description, message, object, event type] respectively.
468
- *
469
- * @throws Exception
470
  */
471
  public function RegisterGroup( $groups ) {
472
  foreach ( $groups as $name => $group ) {
@@ -856,71 +858,6 @@ final class WSAL_AlertManager {
856
  return $is_disabled;
857
  }
858
 
859
- /**
860
- * Return alerts for MainWP Extension.
861
- *
862
- * @param integer $limit - Number of alerts to retrieve.
863
- * @param int|bool $offset - Events offset, otherwise false.
864
- * @param stdClass|bool $query_args - Events query arguments, otherwise false.
865
- *
866
- * @return stdClass
867
- * @throws Freemius_Exception
868
- */
869
- public function get_mainwp_extension_events( $limit = 100, $offset = false, $query_args = false ) {
870
- $mwp_events = new stdClass();
871
-
872
- // Check if limit is not empty.
873
- if ( empty( $limit ) ) {
874
- return $mwp_events;
875
- }
876
-
877
- // Initiate query occurrence object.
878
- $events_query = new WSAL_Models_OccurrenceQuery();
879
- $events_query->addCondition( 'site_id = %s ', 1 ); // Set site id.
880
- $events_query = $this->filter_query( $events_query, $query_args );
881
-
882
- // Check query arguments.
883
- if ( false !== $query_args ) {
884
- if ( isset( $query_args['get_count'] ) && $query_args['get_count'] ) {
885
- $mwp_events->total_items = $events_query->getAdapter()->Count( $events_query );
886
- } else {
887
- $mwp_events->total_items = false;
888
- }
889
- }
890
-
891
- // Set order by.
892
- $events_query->addOrderBy( 'created_on', true );
893
-
894
- // Set the limit.
895
- $events_query->setLimit( $limit );
896
-
897
- // Set the offset.
898
- if ( false !== $offset ) {
899
- $events_query->setOffset( $offset );
900
- }
901
-
902
- // Execute the query.
903
- /** @var \WSAL\MainWPExtension\Models\Occurrence[] $events */
904
- $events = $events_query->getAdapter()->Execute( $events_query );
905
-
906
- if ( ! empty( $events ) && is_array( $events ) ) {
907
- foreach ( $events as $event ) {
908
- // Get event meta.
909
- $meta_data = $event->GetMetaArray();
910
- $meta_data['UserData'] = $this->get_event_user_data( WSAL_Utilities_UsersUtils::GetUsername( $meta_data ) );
911
- $mwp_events->events[ $event->id ] = new stdClass();
912
- $mwp_events->events[ $event->id ]->id = $event->id;
913
- $mwp_events->events[ $event->id ]->alert_id = $event->alert_id;
914
- $mwp_events->events[ $event->id ]->created_on = $event->created_on;
915
- $mwp_events->events[ $event->id ]->meta_data = $meta_data;
916
- }
917
-
918
- $mwp_events->users = $this->wp_users;
919
- }
920
-
921
- return $mwp_events;
922
- }
923
-
924
  /**
925
  * Return user data array of the events.
926
  *
@@ -1215,204 +1152,6 @@ final class WSAL_AlertManager {
1215
  return get_event_type_data( $event_type );
1216
  }
1217
 
1218
- /**
1219
- * Filter query for MWPAL.
1220
- *
1221
- * @param WSAL_Models_OccurrenceQuery $query - Events query.
1222
- * @param array $query_args - Query args.
1223
- *
1224
- * @return WSAL_Models_OccurrenceQuery
1225
- * @throws Freemius_Exception
1226
- */
1227
- private function filter_query( $query, $query_args ) {
1228
- if ( isset( $query_args['search_term'] ) && $query_args['search_term'] ) {
1229
- $query->addSearchCondition( $query_args['search_term'] );
1230
- }
1231
-
1232
- if ( ! empty( $query_args['search_filters'] ) ) {
1233
- // Get DB connection array.
1234
- $connection = WpSecurityAuditLog::GetInstance()->getConnector()->getAdapter( 'Occurrence' )->get_connection();
1235
- $connection->set_charset( $connection->dbh, 'utf8mb4', 'utf8mb4_general_ci' );
1236
-
1237
- // Tables.
1238
- $meta = new WSAL_Adapters_MySQL_Meta( $connection );
1239
- $table_meta = $meta->GetTable(); // Metadata.
1240
- $occurrence = new WSAL_Adapters_MySQL_Occurrence( $connection );
1241
- $table_occ = $occurrence->GetTable(); // Occurrences.
1242
-
1243
- foreach ( $query_args['search_filters'] as $prefix => $value ) {
1244
- if ( 'event' === $prefix ) {
1245
- $query->addORCondition( array( 'alert_id = %s' => $value ) );
1246
- } elseif ( in_array( $prefix, array( 'from', 'to', 'on' ), true ) ) {
1247
- $date = DateTime::createFromFormat( $this->sanitized_date_format, $value[0] );
1248
- $date->setTime( 0, 0 ); // Reset time to 00:00:00.
1249
- $date_string = $date->format( 'U' );
1250
-
1251
- if ( 'from' === $prefix ) {
1252
- $query->addCondition( 'created_on >= %s', $date_string );
1253
- } elseif ( 'to' === $prefix ) {
1254
- $query->addCondition( 'created_on <= %s', strtotime( '+1 day -1 minute', $date_string ) );
1255
- } elseif ( 'on' === $prefix ) {
1256
- $query->addCondition( 'created_on >= %s', strtotime( '-1 day +1 day +1 second', $date_string ) );
1257
- $query->addCondition( 'created_on <= %s', strtotime( '+1 day -1 second', $date_string ) );
1258
- }
1259
- } elseif ( in_array( $prefix, array( 'username', 'firstname', 'lastname' ), true ) ) {
1260
- // User ids array.
1261
- $user_ids = array();
1262
-
1263
- if ( 'username' === $prefix ) {
1264
- foreach ( $value as $username ) {
1265
- $user = get_user_by( 'login', $username );
1266
-
1267
- if ( ! $user ) {
1268
- $user = get_user_by( 'slug', $username );
1269
- }
1270
-
1271
- if ( $user ) {
1272
- $user_ids[] = $user->ID;
1273
- }
1274
- }
1275
- } elseif ( 'firstname' === $prefix || 'lastname' === $prefix ) {
1276
- $users = array();
1277
- $meta_key = 'firstname' === $prefix ? 'first_name' : ( 'lastname' === $prefix ? 'last_name' : false );
1278
-
1279
- foreach ( $value as $name ) {
1280
- $users_array = get_users(
1281
- array(
1282
- 'meta_key' => $meta_key,
1283
- 'meta_value' => $name,
1284
- 'fields' => array( 'ID', 'user_login' ),
1285
- 'meta_compare' => 'LIKE',
1286
- )
1287
- );
1288
-
1289
- foreach ( $users_array as $user ) {
1290
- $users[] = $user;
1291
- }
1292
- }
1293
-
1294
- $usernames = array();
1295
-
1296
- if ( ! empty( $users ) ) {
1297
- foreach ( $users as $user ) {
1298
- $usernames[] = $user->user_login;
1299
- $user_ids[] = $user->ID;
1300
- }
1301
- }
1302
-
1303
- $value = $usernames;
1304
- }
1305
-
1306
- $sql = "$table_occ.id IN ( SELECT occurrence_id FROM $table_meta as meta WHERE ";
1307
-
1308
- if ( ! empty( $user_ids ) ) {
1309
- $last_userid = end( $user_ids );
1310
- $sql .= "( meta.name='CurrentUserID' AND ( ";
1311
-
1312
- foreach ( $user_ids as $user_id ) {
1313
- if ( $last_userid === $user_id ) {
1314
- $sql .= "meta.value='$user_id'";
1315
- } else {
1316
- $sql .= "meta.value='$user_id' OR ";
1317
- }
1318
- }
1319
-
1320
- $sql .= ' ) )';
1321
- $sql .= ' OR ';
1322
- }
1323
-
1324
- if ( ! empty( $value ) ) {
1325
- $last_username = end( $value );
1326
- $sql .= "( meta.name='Username' AND ( ";
1327
-
1328
- foreach ( $value as $username ) {
1329
- if ( $last_username === $username ) {
1330
- $sql .= "meta.value='%s'";
1331
- } else {
1332
- $sql .= "meta.value='$username' OR ";
1333
- }
1334
- }
1335
-
1336
- $sql .= ' ) )';
1337
- }
1338
-
1339
- $sql .= ' )';
1340
- $user_count = count( $value );
1341
-
1342
- if ( $user_count ) {
1343
- $query->addORCondition( array( $sql => $value[ $user_count - 1 ] ) );
1344
- } else {
1345
- $query->addORCondition( array( $sql => '' ) );
1346
- }
1347
- } elseif ( 'userrole' === $prefix ) {
1348
- // User role search condition.
1349
- $sql = "$table_occ.id IN ( SELECT occurrence_id FROM $table_meta as meta WHERE meta.name='CurrentUserRoles' AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP %s )";
1350
- $value = implode( '|', $value );
1351
- $query->addORCondition( array( $sql => $value ) );
1352
- } elseif ( in_array( $prefix, array( 'posttype', 'poststatus', 'postid', 'postname' ), true ) ) {
1353
- $post_meta = '';
1354
-
1355
- if ( 'posttype' === $prefix ) {
1356
- $post_meta = 'PostType';
1357
- } elseif ( 'poststatus' === $prefix ) {
1358
- $post_meta = 'PostStatus';
1359
- } elseif ( 'postid' === $prefix ) {
1360
- $post_meta = 'PostID';
1361
- } elseif ( 'postname' === $prefix ) {
1362
- $post_meta = 'PostTitle';
1363
- }
1364
-
1365
- // Post meta search condition.
1366
- $sql = "$table_occ.id IN ( SELECT occurrence_id FROM $table_meta as meta WHERE meta.name='$post_meta' AND ( ";
1367
- if ( 'postname' === $prefix ) {
1368
- $value = array_map( array( $this, 'add_string_wildcards' ), $value );
1369
- }
1370
-
1371
- // Get the last value.
1372
- $last_value = end( $value );
1373
-
1374
- foreach ( $value as $post_meta ) {
1375
- if ( $last_value === $post_meta ) {
1376
- continue;
1377
- }
1378
-
1379
- if ( 'postname' === $prefix ) {
1380
- $sql .= "( (meta.value LIKE '$post_meta') > 0 ) OR ";
1381
- } else {
1382
- $sql .= "meta.value='$post_meta' OR ";
1383
- }
1384
- }
1385
-
1386
- // Add placeholder for the last value.
1387
- if ( 'postname' === $prefix ) {
1388
- $sql .= "( (meta.value LIKE '%s') > 0 ) ) )";
1389
- } else {
1390
- $sql .= "meta.value='%s' ) )";
1391
- }
1392
-
1393
- $query->addORCondition( array( $sql => $last_value ) );
1394
- } elseif ( 'ip' === $prefix ) {
1395
- // IP search condition.
1396
- $sql = "$table_occ.id IN ( SELECT occurrence_id FROM $table_meta as meta WHERE meta.name='ClientIP' AND ( ";
1397
- $count = count( $value );
1398
-
1399
- foreach ( $value as $ip ) {
1400
- if ( $value[ $count - 1 ] === $ip ) {
1401
- $sql .= "meta.value='%s'";
1402
- } else {
1403
- $sql .= "meta.value='$ip' OR ";
1404
- }
1405
- }
1406
-
1407
- $sql .= ' ) )';
1408
- $query->addORCondition( array( $sql => $value[ $count - 1 ] ) );
1409
- }
1410
- }
1411
- }
1412
-
1413
- return $query;
1414
- }
1415
-
1416
  /**
1417
  * Modify post name values to include MySQL wildcards.
1418
  *
@@ -1454,130 +1193,6 @@ final class WSAL_AlertManager {
1454
  return array_keys( $this->get_sub_categorized_events() );
1455
  }
1456
 
1457
- /**
1458
- * Generate report matching the filter passed.
1459
- *
1460
- * @param array $filters - Filters.
1461
- * @param mixed $report_type - Type of report.
1462
- * @return stdClass
1463
- */
1464
- public function get_mainwp_extension_report( array $filters, $report_type ) {
1465
- // Check report type.
1466
- if ( ! $report_type ) {
1467
- $report = new stdClass();
1468
- $report->data = array();
1469
-
1470
- do {
1471
- $response = $this->generate_report( $filters );
1472
-
1473
- if ( isset( $response['data'] ) ) {
1474
- $report->data = array_merge( $report->data, $response['data'] );
1475
- }
1476
-
1477
- // Set the filters next date.
1478
- $filters['nextDate'] = ( isset( $response['lastDate'] ) && $response['lastDate'] ) ? $response['lastDate'] : 0;
1479
- } while ( $filters['nextDate'] );
1480
- } elseif ( 'statistics_unique_ips' === $report_type ) {
1481
- $report = new stdClass();
1482
- $report->data = $this->generate_statistics_unique_ips( $filters );
1483
- }
1484
-
1485
- return $report;
1486
- }
1487
-
1488
- /**
1489
- * Generate report for MainWP extension.
1490
- *
1491
- * @param array $filters - Filters.
1492
- * @return array
1493
- */
1494
- private function generate_report( $filters ) {
1495
- // check the report format
1496
- $report_format = empty( $filters['report-format'] ) ? 'html' : 'csv';
1497
- if ( ! in_array( $report_format, array( 'csv', 'html' ), true ) ) {
1498
- return false;
1499
- }
1500
-
1501
- // some alert codes or alert groups are needed to run a report
1502
- if ( empty( $filters['alert-codes']['groups'] ) && empty( $filters['alert-codes']['codes'] ) ) {
1503
- return false;
1504
- }
1505
-
1506
- $args = WSAL_ReportArgs::build_from_alternative_filters( $filters, $this );
1507
-
1508
- $next_date = empty( $filters['nextDate'] ) ? null : $filters['nextDate'];
1509
- $limit = empty( $filters['limit'] ) ? 0 : $filters['limit'];
1510
-
1511
- $last_date = null;
1512
-
1513
- if ( isset( $filters['unique-ip'] ) && $filters['unique-ip'] ) {
1514
- $results = $this->plugin->getConnector()->getAdapter( 'Occurrence' )->GetReportGrouped( $args );
1515
- } else {
1516
- $results = $this->plugin->getConnector()->getAdapter( 'Occurrence' )->GetReporting( $args, $next_date, $limit );
1517
- }
1518
-
1519
- if ( ! empty( $results['lastDate'] ) ) {
1520
- $last_date = $results['lastDate'];
1521
- unset( $results['lastDate'] );
1522
- }
1523
-
1524
- if ( empty( $results ) ) {
1525
- return false;
1526
- }
1527
-
1528
- $data = array();
1529
- $data_and_filters = array();
1530
-
1531
- if ( ! empty( $filters['unique-ip'] ) ) {
1532
- $data = array_values( $results );
1533
- } else {
1534
- // Get alert details.
1535
- foreach ( $results as $entry ) {
1536
- $ip = esc_html( $entry->ip );
1537
- $ua = esc_html( $entry->ua );
1538
- $roles = maybe_unserialize( $entry->roles );
1539
-
1540
- if ( is_array( $roles ) ) {
1541
- $roles = implode( ', ', $roles );
1542
- } else {
1543
- $roles = '';
1544
- }
1545
-
1546
- if ( 9999 === (int) $entry->alert_id ) {
1547
- continue;
1548
- }
1549
-
1550
- $t = $this->get_alert_details( $entry->id, $entry->id, $entry->alert_id, $entry->site_id, $entry->created_on, $entry->user_id, $roles, $ip, $ua, 'report-' . $report_format);
1551
- array_push( $data, $t );
1552
- }
1553
- }
1554
-
1555
- if ( empty( $data ) ) {
1556
- return false;
1557
- }
1558
-
1559
- $data_and_filters['data'] = $data;
1560
- $data_and_filters['filters'] = $filters;
1561
- $data_and_filters['lastDate'] = $last_date;
1562
-
1563
- return $data_and_filters;
1564
- }
1565
-
1566
- /**
1567
- * Create statistics unique IPs report.
1568
- *
1569
- * @param array $filters - Filters.
1570
- *
1571
- * @return array
1572
- * @throws Freemius_Exception
1573
- */
1574
- private function generate_statistics_unique_ips( $filters ) {
1575
- $report_args = WSAL_ReportArgs::build_from_alternative_filters( $filters, $this );
1576
- $results = $this->plugin->getConnector()->getAdapter( 'Occurrence' )->GetReportGrouped( $report_args );
1577
-
1578
- return array_values( $results );
1579
- }
1580
-
1581
  /**
1582
  * Get user ids for reports.
1583
  *
@@ -1695,23 +1310,32 @@ final class WSAL_AlertManager {
1695
  /**
1696
  * Get alert details.
1697
  *
1698
- * @param int $entry_id - Entry ID.
1699
- * @param int $alert_id - Alert ID.
1700
- * @param int $site_id - Site ID.
1701
- * @param string $created_on - Alert generation time.
1702
- * @param int $user_id - User id.
1703
- * @param string|array $roles - User roles.
1704
- * @param string $ip - IP address of the user.
1705
- * @param string $ua - User agent.
1706
  *
1707
- * @return array|false details
1708
  * @throws Exception
1709
  */
1710
- private function get_alert_details( $entry_id, $alert_id, $site_id, $created_on, $user_id = null, $roles = null, $ip = '', $ua = '', $context = 'default' ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1711
  // Must be a new instance every time, otherwise the alert message is not retrieved properly.
1712
  $occurrence = new WSAL_Models_Occurrence();
1713
 
1714
- $user_id = ( ! is_numeric( $user_id ) && null !== $user_id ) ? WSAL_Rep_Util_S::swap_login_for_id( $user_id ) : $user_id;
1715
 
1716
  // Get alert details.
1717
  $code = $this->GetAlert( $alert_id );
@@ -1723,33 +1347,22 @@ final class WSAL_AlertManager {
1723
  );
1724
  $const = $this->plugin->constants->GetConstantBy( 'value', $code, $const );
1725
 
1726
- // Blog details.
1727
- if ( $this->plugin->IsMultisite() ) {
1728
- $blog_info = get_blog_details( $site_id, true );
1729
- $blog_name = esc_html__( 'Unknown Site', 'wp-security-audit-log' );
1730
- $blog_url = '';
1731
-
1732
- if ( $blog_info ) {
1733
- $blog_name = esc_html( $blog_info->blogname );
1734
- $blog_url = esc_attr( $blog_info->siteurl );
1735
- }
1736
- } else {
1737
- $blog_name = get_bloginfo( 'name' );
1738
- $blog_url = '';
1739
-
1740
- if ( empty( $blog_name ) ) {
1741
- $blog_name = __( 'Unknown Site', 'wp-security-audit-log' );
1742
- } else {
1743
- $blog_name = esc_html( $blog_name );
1744
- $blog_url = esc_attr( get_bloginfo( 'url' ) );
1745
- }
1746
- }
1747
 
1748
  // Get the alert message - properly.
1749
- $occurrence->id = $entry_id;
1750
- $occurrence->site_id = $site_id;
1751
- $occurrence->alert_id = $alert_id;
1752
- $occurrence->created_on = $created_on;
 
 
 
 
 
 
 
 
 
1753
 
1754
  $event_metadata = $occurrence->GetMetaArray();
1755
  if ( ! $occurrence->_cachedMessage ) {
@@ -1766,18 +1379,74 @@ final class WSAL_AlertManager {
1766
  // Meta details.
1767
  return array(
1768
  'site_id' => $site_id,
1769
- 'blog_name' => $blog_name,
1770
- 'blog_url' => $blog_url,
1771
  'alert_id' => $alert_id,
1772
- 'timestamp' => $created_on,
1773
  'date' => WSAL_Utilities_DateTimeFormatter::instance()->getFormattedDateTime( $created_on ),
 
 
 
1774
  'code' => $const->name,
 
1775
  'message' => $occurrence->GetMessage( $event_metadata, $context ),
 
1776
  'user_name' => $username,
1777
  'user_data' => $user_id ? $this->get_event_user_data( $username ) : false,
1778
  'role' => $roles,
1779
  'user_ip' => $ip,
 
 
1780
  'user_agent' => $ua,
1781
  );
1782
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1783
  }
83
  public $ignored_cpts = array();
84
 
85
  /**
86
+ * Date format.
87
+ *
88
  * @var string Date format.
89
  */
90
  private $date_format;
91
 
92
+ /**
93
+ * Sanitized date format.
94
+ *
95
+ * @var string
96
+ * @since 4.2.1
97
+ */
98
+ private $sanitized_date_format;
99
 
100
  /**
101
  * Create new AlertManager instance.
104
  */
105
  public function __construct( WpSecurityAuditLog $plugin ) {
106
  $this->plugin = $plugin;
107
+ foreach ( WSAL_Utilities_FileSystemUtils::read_files_in_folder( dirname( __FILE__ ) . '/Loggers', '*.php' ) as $file ) {
108
  $this->AddFromFile( $file );
109
  }
110
 
237
 
238
  // If user or user role is enable then go ahead.
239
  if ( $this->CheckEnableUserRoles( $username, $roles ) ) {
240
+
241
+ $data['Timestamp'] = ( isset( $data['Timestamp'] ) && ! empty( $data['Timestamp'] ) ) ? $data['Timestamp'] : current_time( 'U.u', 'true' );
242
  if ( $delayed ) {
243
  $this->TriggerIf( $type, $data, null );
244
  } else {
335
  /**
336
  * Method: Commit an alert now.
337
  *
338
+ * @param int $type - Alert type.
339
+ * @param array $data - Data of the alert.
340
+ * @param array $cond - Condition for the alert.
341
+ * @param bool $_retry - Retry.
342
  *
343
  * @return mixed
 
344
  * @internal
 
345
  */
346
  protected function _CommitItem( $type, $data, $cond, $_retry = true ) {
347
  // Double NOT operation here is intentional. Same as ! ( bool ) [ $value ]
359
  } else {
360
  // In general this shouldn't happen, but it could, so we handle it here.
361
  /* translators: Event ID */
362
+ $error_message = sprintf( esc_html__( 'Event with code %d has not be registered.', 'wp-security-audit-log' ), $type );
363
+ $this->plugin->wsal_log( $error_message );
364
  }
365
  }
366
  }
411
  /**
412
  * Register an alert type.
413
  *
414
+ * @param string $category Category name.
415
  * @param string $subcategory Subcategory name.
416
+ * @param array $info Event information from defaults.php.
 
 
417
  */
418
  public function Register( $category, $subcategory, $info ) {
419
 
420
+ // Default for optional fields.
421
+ $metadata = array();
422
+ $links = array();
423
  $object = '';
424
  $event_type = '';
425
 
426
  $definition_items_count = count( $info );
427
+ if ( 8 === $definition_items_count ) {
428
+ // Most recent event definition introduced in version 4.2.1.
429
  list( $code, $severity, $desc, $message, $metadata, $links, $object, $event_type ) = $info;
430
+ } elseif ( 6 === $definition_items_count ) {
431
+ // Legacy event definition for backwards compatibility (used prior to version 4.2.1).
432
  list( $code, $severity, $desc, $message, $object, $event_type ) = $info;
433
  } else {
434
+ // Even older legacy event definition for backwards compatibility.
435
  list( $code, $severity, $desc, $message ) = $info;
436
  }
437
 
438
  if ( is_string( $links ) ) {
439
+ $links = array( $links );
440
  }
441
+
442
  if ( isset( $this->_alerts[ $code ] ) ) {
443
  add_action( 'admin_notices', array( $this, 'duplicate_event_notice' ) );
444
  /* Translators: Event ID */
445
+ $error_message = sprintf( esc_html__( 'Event %s already registered with WP Activity Log.', 'wp-security-audit-log' ), $code );
446
+ $this->plugin->wsal_log( $error_message );
447
+
448
+ return;
449
  }
450
 
451
  /**
458
  * @param integer $code - Event ID.
459
  *
460
  * @since 4.3.2
 
461
  */
462
  $metadata = apply_filters( 'wsal_event_metadata_definition', $metadata, $code );
463
 
469
  *
470
  * @param array $groups - An array with group name as the index and an array of group items as the value.
471
  * Item values is an array of [type, code, description, message, object, event type] respectively.
 
 
472
  */
473
  public function RegisterGroup( $groups ) {
474
  foreach ( $groups as $name => $group ) {
858
  return $is_disabled;
859
  }
860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  /**
862
  * Return user data array of the events.
863
  *
1152
  return get_event_type_data( $event_type );
1153
  }
1154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1155
  /**
1156
  * Modify post name values to include MySQL wildcards.
1157
  *
1193
  return array_keys( $this->get_sub_categorized_events() );
1194
  }
1195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1196
  /**
1197
  * Get user ids for reports.
1198
  *
1310
  /**
1311
  * Get alert details.
1312
  *
1313
+ * @param stdClass $entry Raw entry from the occurrences table.
1314
+ * @param string $context Display context.
 
 
 
 
 
 
1315
  *
1316
+ * @return array|false Alert details.
1317
  * @throws Exception
1318
  */
1319
+ public function get_alert_details( $entry, $context = 'default' ) {
1320
+ $entry_id = $entry->id;
1321
+ $alert_id = $entry->alert_id;
1322
+ $site_id = $entry->site_id;
1323
+ $created_on = $entry->created_on;
1324
+ $object = $entry->object;
1325
+ $event_type = $entry->event_type;
1326
+ $user_id = $entry->user_id;
1327
+
1328
+ $ip = esc_html( $entry->ip );
1329
+ $ua = esc_html( $entry->ua );
1330
+ $roles = maybe_unserialize( $entry->roles );
1331
+ if ( is_string( $roles ) ) {
1332
+ $roles = str_replace( array( '"', '[', ']' ), ' ', $roles );
1333
+ }
1334
+
1335
  // Must be a new instance every time, otherwise the alert message is not retrieved properly.
1336
  $occurrence = new WSAL_Models_Occurrence();
1337
 
1338
+ $user_id = ( ! is_numeric( $user_id ) && null !== $user_id ) ? WSAL_Utilities_UsersUtils::swap_login_for_id( $user_id ) : $user_id;
1339
 
1340
  // Get alert details.
1341
  $code = $this->GetAlert( $alert_id );
1347
  );
1348
  $const = $this->plugin->constants->GetConstantBy( 'value', $code, $const );
1349
 
1350
+ $blog_info = WSAL_AlertManager::get_blog_info( $this->plugin, $site_id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1351
 
1352
  // Get the alert message - properly.
1353
+ $occurrence->id = $entry_id;
1354
+ $occurrence->site_id = $site_id;
1355
+ $occurrence->alert_id = $alert_id;
1356
+ $occurrence->created_on = $created_on;
1357
+ $occurrence->client_ip = $ip;
1358
+ $occurrence->object = $object;
1359
+ $occurrence->event_type = $event_type;
1360
+ $occurrence->user_id = $user_id;
1361
+ $occurrence->user_agent = $ua;
1362
+ $occurrence->post_id = $entry->post_id;
1363
+ $occurrence->post_type = $entry->post_type;
1364
+ $occurrence->post_status = $entry->post_status;
1365
+ $occurrence->SetUserRoles( $roles );
1366
 
1367
  $event_metadata = $occurrence->GetMetaArray();
1368
  if ( ! $occurrence->_cachedMessage ) {
1379
  // Meta details.
1380
  return array(
1381
  'site_id' => $site_id,
1382
+ 'blog_name' => $blog_info['name'],
1383
+ 'blog_url' => $blog_info['url'],
1384
  'alert_id' => $alert_id,
 
1385
  'date' => WSAL_Utilities_DateTimeFormatter::instance()->getFormattedDateTime( $created_on ),
1386
+ // We need to keep the timestamp to be able to group entries by dates etc. The "date" field is not suitable
1387
+ // as it is already translated, thus difficult to parse and process.
1388
+ 'timestamp' => $created_on,
1389
  'code' => $const->name,
1390
+ // Fill variables in message.
1391
  'message' => $occurrence->GetMessage( $event_metadata, $context ),
1392
+ 'user_id' => $user_id,
1393
  'user_name' => $username,
1394
  'user_data' => $user_id ? $this->get_event_user_data( $username ) : false,
1395
  'role' => $roles,
1396
  'user_ip' => $ip,
1397
+ 'object' => $this->get_event_objects_data( $object ),
1398
+ 'event_type' => $this->get_event_type_data( $event_type ),
1399
  'user_agent' => $ua,
1400
  );
1401
  }
1402
+
1403
+ /**
1404
+ * Retrieves blog info for given site based on current multisite situation. Optimizes for performance using local
1405
+ * cache.
1406
+ *
1407
+ * @param WpSecurityAuditLog $plugin WSAL plugin instance.
1408
+ * @param int $site_id Site ID.
1409
+ *
1410
+ * @return array
1411
+ * @since 4.4.0
1412
+ */
1413
+ public static function get_blog_info( $plugin, $site_id ) {
1414
+ // Blog details.
1415
+ if ( $plugin->IsMultisite() ) {
1416
+ $blog_info = get_blog_details( $site_id, true );
1417
+ $blog_name = esc_html__( 'Unknown Site', 'wp-security-audit-log' );
1418
+ $blog_url = '';
1419
+
1420
+ if ( $blog_info ) {
1421
+ $blog_name = esc_html( $blog_info->blogname );
1422
+ $blog_url = esc_attr( $blog_info->siteurl );
1423
+ }
1424
+ } else {
1425
+ $blog_name = get_bloginfo( 'name' );
1426
+ $blog_url = '';
1427
+
1428
+ if ( empty( $blog_name ) ) {
1429
+ $blog_name = __( 'Unknown Site', 'wp-security-audit-log' );
1430
+ } else {
1431
+ $blog_name = esc_html( $blog_name );
1432
+ $blog_url = esc_attr( get_bloginfo( 'url' ) );
1433
+ }
1434
+ }
1435
+
1436
+ return array(
1437
+ 'name' => $blog_name,
1438
+ 'url' => $blog_url,
1439
+ );
1440
+ }
1441
+
1442
+ /**
1443
+ * Retrieves local cache of WP Users.
1444
+ *
1445
+ * @return WP_User[] WordPress users.
1446
+ *
1447
+ * @since 4.4.0
1448
+ */
1449
+ public function get_wp_users(): array {
1450
+ return $this->wp_users;
1451
+ }
1452
  }
classes/AuditLogGridView.php CHANGED
@@ -418,10 +418,8 @@ class WSAL_AuditLogGridView extends WP_List_Table {
418
 
419
  // Additional user info tooltip.
420
  $tooltip = WSAL_Utilities_UsersUtils::get_tooltip_user_content( $user );
421
-
422
  $uhtml = '<a class="tooltip" data-tooltip="' . esc_attr( $tooltip ) . '" data-user="' . $user->user_login . '" href="' . $user_edit_link . '" target="_blank">' . esc_html( $display_name ) . '</a>';
423
 
424
-
425
  $roles = WSAL_Utilities_UsersUtils::get_roles_label( $item->GetUserRoles() );
426
  } elseif ( 'Plugin' == $username ) {
427
  $uhtml = '<i>' . __( 'Plugin', 'wp-security-audit-log' ) . '</i>';
@@ -784,7 +782,7 @@ class WSAL_AuditLogGridView extends WP_List_Table {
784
  */
785
  public function query_events( $paged = 0 ) {
786
 
787
- // TO DO: Get rid of OccurrenceQuery and use the Occurence Model.
788
  $query = new WSAL_Models_OccurrenceQuery();
789
 
790
  $bid = (int) $this->query_args->site_id;
@@ -848,6 +846,7 @@ class WSAL_AuditLogGridView extends WP_List_Table {
848
 
849
  $query->setOffset( $offset ); // Set query offset.
850
  $query->setLimit( $per_page ); // Set number of events per page.
 
851
  return array(
852
  'total_items' => $total_items,
853
  'per_page' => $per_page,
418
 
419
  // Additional user info tooltip.
420
  $tooltip = WSAL_Utilities_UsersUtils::get_tooltip_user_content( $user );
 
421
  $uhtml = '<a class="tooltip" data-tooltip="' . esc_attr( $tooltip ) . '" data-user="' . $user->user_login . '" href="' . $user_edit_link . '" target="_blank">' . esc_html( $display_name ) . '</a>';
422
 
 
423
  $roles = WSAL_Utilities_UsersUtils::get_roles_label( $item->GetUserRoles() );
424
  } elseif ( 'Plugin' == $username ) {
425
  $uhtml = '<i>' . __( 'Plugin', 'wp-security-audit-log' ) . '</i>';
782
  */
783
  public function query_events( $paged = 0 ) {
784
 
785
+ // TO DO: Get rid of OccurrenceQuery and use the Occurrence Model.
786
  $query = new WSAL_Models_OccurrenceQuery();
787
 
788
  $bid = (int) $this->query_args->site_id;
846
 
847
  $query->setOffset( $offset ); // Set query offset.
848
  $query->setLimit( $per_page ); // Set number of events per page.
849
+
850
  return array(
851
  'total_items' => $total_items,
852
  'per_page' => $per_page,
classes/AuditLogListView.php CHANGED
@@ -768,7 +768,7 @@ class WSAL_AuditLogListView extends WP_List_Table {
768
 
769
  // TO DO: Get rid of OccurrenceQuery and use the Occurrence Model.
770
  $query = new WSAL_Models_OccurrenceQuery();
771
- $bid = (int) $this->query_args->site_id;
772
  if ( $bid ) {
773
  $query->addCondition( 'site_id = %s ', $bid );
774
  }
@@ -808,34 +808,24 @@ class WSAL_AuditLogListView extends WP_List_Table {
808
 
809
  // TO DO: Allow order by meta values.
810
  if ( 'scip' === $order_by ) {
811
- $query->addMetaJoin(); // Since LEFT JOIN clause causes the result values to duplicate.
812
- $query->addCondition( 'meta.name = %s', 'ClientIP' ); // A where condition is added to make sure that we're only requesting the relevant meta data rows from metadata table.
813
- $query->addOrderBy( 'CASE WHEN meta.name = "ClientIP" THEN meta.value END', $is_descending );
814
  } elseif ( 'user' === $order_by ) {
815
- $query->addMetaJoin(); // Since LEFT JOIN clause causes the result values to duplicate.
816
- $query->addCondition( 'meta.name = %s', 'CurrentUserID' ); // A where condition is added to make sure that we're only requesting the relevant meta data rows from metadata table.
817
- $query->addOrderBy( 'CASE WHEN meta.name = "CurrentUserID" THEN meta.value END', $is_descending );
818
  } elseif ( 'code' === $order_by ) {
819
  /*
820
  * Handle the 'code' (Severity) column sorting.
821
  */
822
- $query->addMetaJoin(); // Since LEFT JOIN clause causes the result values to duplicate.
823
- $query->addCondition( 'meta.name = %s', 'Severity' ); // A where condition is added to make sure that we're only requesting the relevant meta data rows from metadata table.
824
- $query->addOrderBy( 'CASE WHEN meta.name = "Severity" THEN meta.value END', $is_descending );
825
  } elseif ( 'object' === $order_by ) {
826
  /*
827
  * Handle the 'object' column sorting.
828
  */
829
- $query->addMetaJoin(); // Since LEFT JOIN clause causes the result values to duplicate.
830
- $query->addCondition( 'meta.name = %s', 'Object' ); // A where condition is added to make sure that we're only requesting the relevant meta data rows from metadata table.
831
- $query->addOrderBy( 'CASE WHEN meta.name = "Object" THEN meta.value END', $is_descending );
832
  } elseif ( 'event_type' === $order_by ) {
833
  /*
834
  * Handle the 'Event Type' column sorting.
835
  */
836
- $query->addMetaJoin(); // Since LEFT JOIN clause causes the result values to duplicate.
837
- $query->addCondition( 'meta.name = %s', 'EventType' ); // A where condition is added to make sure that we're only requesting the relevant meta data rows from metadata table.
838
- $query->addOrderBy( 'CASE WHEN meta.name = "EventType" THEN meta.value END', $is_descending );
839
  } else {
840
  $tmp = new WSAL_Models_Occurrence();
841
  // Making sure the field exists to order by.
768
 
769
  // TO DO: Get rid of OccurrenceQuery and use the Occurrence Model.
770
  $query = new WSAL_Models_OccurrenceQuery();
771
+ $bid = (int) $this->query_args->site_id;
772
  if ( $bid ) {
773
  $query->addCondition( 'site_id = %s ', $bid );
774
  }
808
 
809
  // TO DO: Allow order by meta values.
810
  if ( 'scip' === $order_by ) {
811
+ $query->addOrderBy( 'client_ip', $is_descending );
 
 
812
  } elseif ( 'user' === $order_by ) {
813
+ $query->addOrderBy( 'user_id', $is_descending );
 
 
814
  } elseif ( 'code' === $order_by ) {
815
  /*
816
  * Handle the 'code' (Severity) column sorting.
817
  */
818
+ $query->addOrderBy( 'severity', $is_descending );
 
 
819
  } elseif ( 'object' === $order_by ) {
820
  /*
821
  * Handle the 'object' column sorting.
822
  */
823
+ $query->addOrderBy( 'object', $is_descending );
 
 
824
  } elseif ( 'event_type' === $order_by ) {
825
  /*
826
  * Handle the 'Event Type' column sorting.
827
  */
828
+ $query->addOrderBy( 'event_type', $is_descending );
 
 
829
  } else {
830
  $tmp = new WSAL_Models_Occurrence();
831
  // Making sure the field exists to order by.
classes/Connector/ConnectorFactory.php CHANGED
@@ -28,12 +28,14 @@ abstract class WSAL_Connector_ConnectorFactory {
28
  * @var string
29
  */
30
  public static $adapter;
 
31
  /**
32
  * Connector.
33
  *
34
  * @var array
35
  */
36
- private static $connectors = [];
 
37
  /**
38
  * Occurrence is installed.
39
  *
@@ -44,7 +46,16 @@ abstract class WSAL_Connector_ConnectorFactory {
44
  private static $is_installed;
45
 
46
  /**
47
- * Returns the a default WPDB connector that must be always used for some data, for example user sessions and
 
 
 
 
 
 
 
 
 
48
  * also custom options table in the past.
49
  */
50
  public static function GetDefaultConnector() {
@@ -55,7 +66,7 @@ abstract class WSAL_Connector_ConnectorFactory {
55
  * Returns a connector singleton
56
  *
57
  * @param string|array $config DB configuration array, db alias or empty to use default connection.
58
- * @param bool $reset - True if reset.
59
  *
60
  * @return WSAL_Connector_ConnectorInterface
61
  * @throws Freemius_Exception
@@ -63,17 +74,24 @@ abstract class WSAL_Connector_ConnectorFactory {
63
  public static function GetConnector( $config = null, $reset = false ) {
64
  $connection_config = null;
65
  if ( is_null( $config ) || empty( $config ) ) {
66
- // default config - local or external, depending on plugin settings and licensing
67
- $connection_config = self::GetConfig( $config );
 
 
 
 
 
 
 
68
  } else {
69
  if ( is_string( $config ) ) {
70
- // string based config, can be used to retrieve local WP connection
71
- if ( 'local' === $config ) {
72
- // this forces the WSAL_Connector_MySQLDB to return connection to local WP database
73
- $connection_config = null;
74
- }
75
- } else if ( is_array( $config ) ) {
76
- // array config gets connection to whatever database configuration it holds
77
  $connection_config = $config;
78
  }
79
  }
@@ -81,23 +99,23 @@ abstract class WSAL_Connector_ConnectorFactory {
81
  $cache_key = 'default';
82
  if ( is_string( $config ) ) {
83
  $cache_key = $connection_config;
84
- } else if ( is_array( $connection_config ) ) {
85
  $cache_key = $connection_config['name'];
86
  }
87
 
88
  // TO DO: Load connection config.
89
- if ( ! array_key_exists($cache_key, self::$connectors) || $reset ) {
90
  $connection_type = is_array( $connection_config ) && isset( $connection_config['type'] ) ? strtolower( $connection_config['type'] ) : '';
91
  switch ( $connection_type ) {
92
  // TO DO: Add other connectors.
93
  case 'mysql':
94
  default:
95
  // Use config.
96
- self::$connectors[$cache_key] = new WSAL_Connector_MySQLDB( $connection_config );
97
  }
98
  }
99
 
100
- return self::$connectors[$cache_key];
101
  }
102
 
103
  /**
@@ -131,11 +149,24 @@ abstract class WSAL_Connector_ConnectorFactory {
131
  return null;
132
  }
133
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  /*
135
  * Reused code from the external DB module.
136
  *
137
  * @see WSAL_Ext_Common::get_connection()
138
  */
 
139
  $connection_raw = maybe_unserialize( $plugin->GetGlobalSetting( 'connection-' . $connection_name ) );
140
  $connection = ( $connection_raw instanceof stdClass ) ? json_decode( json_encode( $connection_raw ), true ) : $connection_raw;
141
  if ( ! is_array( $connection ) || empty( $connection ) ) {
@@ -153,7 +184,7 @@ abstract class WSAL_Connector_ConnectorFactory {
153
  * @return boolean true|false
154
  */
155
  public static function CheckConfig( $config ) {
156
- // only mysql supported at the moment
157
  if ( array_key_exists( 'type', $config ) && 'mysql' === $config['type'] ) {
158
  try {
159
  $connector = new WSAL_Connector_MySQLDB( $config );
@@ -165,4 +196,22 @@ abstract class WSAL_Connector_ConnectorFactory {
165
 
166
  return false;
167
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
28
  * @var string
29
  */
30
  public static $adapter;
31
+
32
  /**
33
  * Connector.
34
  *
35
  * @var array
36
  */
37
+ private static $connectors = array();
38
+
39
  /**
40
  * Occurrence is installed.
41
  *
46
  private static $is_installed;
47
 
48
  /**
49
+ * Enabled archive mode. It forces archive connector by default.
50
+ *
51
+ * @var bool
52
+ *
53
+ * @since 4.4.0
54
+ */
55
+ private static $archive_mode = false;
56
+
57
+ /**
58
+ * Returns the default WPDB connector that must be always used for some data, for example user sessions and
59
  * also custom options table in the past.
60
  */
61
  public static function GetDefaultConnector() {
66
  * Returns a connector singleton
67
  *
68
  * @param string|array $config DB configuration array, db alias or empty to use default connection.
69
+ * @param bool $reset - True if reset.
70
  *
71
  * @return WSAL_Connector_ConnectorInterface
72
  * @throws Freemius_Exception
74
  public static function GetConnector( $config = null, $reset = false ) {
75
  $connection_config = null;
76
  if ( is_null( $config ) || empty( $config ) ) {
77
+ if ( self::$archive_mode ) {
78
+ // Force archive database if no config provided and archive mode is enabled.
79
+ $plugin = WpSecurityAuditLog::GetInstance();
80
+ $connection_name = $plugin->GetGlobalSetting( 'archive-connection' );
81
+ $connection_config = self::load_connection_config( $connection_name );
82
+ } else {
83
+ // Default config - local or external, depending on plugin settings and licensing.
84
+ $connection_config = self::GetConfig( $config );
85
+ }
86
  } else {
87
  if ( is_string( $config ) ) {
88
+ // String based config, can be used to retrieve local WP connection.
89
+ if ( 'local' === $config ) {
90
+ // This forces the WSAL_Connector_MySQLDB to return connection to local WP database.
91
+ $connection_config = null;
92
+ }
93
+ } elseif ( is_array( $config ) ) {
94
+ // Array config gets connection to whatever database configuration it holds.
95
  $connection_config = $config;
96
  }
97
  }
99
  $cache_key = 'default';
100
  if ( is_string( $config ) ) {
101
  $cache_key = $connection_config;
102
+ } elseif ( is_array( $connection_config ) ) {
103
  $cache_key = $connection_config['name'];
104
  }
105
 
106
  // TO DO: Load connection config.
107
+ if ( ! array_key_exists( $cache_key, self::$connectors ) || $reset ) {
108
  $connection_type = is_array( $connection_config ) && isset( $connection_config['type'] ) ? strtolower( $connection_config['type'] ) : '';
109
  switch ( $connection_type ) {
110
  // TO DO: Add other connectors.
111
  case 'mysql':
112
  default:
113
  // Use config.
114
+ self::$connectors[ $cache_key ] = new WSAL_Connector_MySQLDB( $connection_config );
115
  }
116
  }
117
 
118
+ return self::$connectors[ $cache_key ];
119
  }
120
 
121
  /**
149
  return null;
150
  }
151
 
152
+ return self::load_connection_config( $connection_name );
153
+ }
154
+
155
+ /**
156
+ * Loads connection config using its name.
157
+ *
158
+ * @param string $connection_name Connection name.
159
+ *
160
+ * @return array|null
161
+ * @since 4.4.0
162
+ */
163
+ private static function load_connection_config( $connection_name ) {
164
  /*
165
  * Reused code from the external DB module.
166
  *
167
  * @see WSAL_Ext_Common::get_connection()
168
  */
169
+ $plugin = WpSecurityAuditLog::GetInstance();
170
  $connection_raw = maybe_unserialize( $plugin->GetGlobalSetting( 'connection-' . $connection_name ) );
171
  $connection = ( $connection_raw instanceof stdClass ) ? json_decode( json_encode( $connection_raw ), true ) : $connection_raw;
172
  if ( ! is_array( $connection ) || empty( $connection ) ) {
184
  * @return boolean true|false
185
  */
186
  public static function CheckConfig( $config ) {
187
+ // Only mysql supported at the moment.
188
  if ( array_key_exists( 'type', $config ) && 'mysql' === $config['type'] ) {
189
  try {
190
  $connector = new WSAL_Connector_MySQLDB( $config );
196
 
197
  return false;
198
  }
199
+
200
+ /**
201
+ * Enables archive mode.
202
+ *
203
+ * @since 4.4.0
204
+ */
205
+ public static function enable_archive_mode() {
206
+ self::$archive_mode = true;
207
+ }
208
+
209
+ /**
210
+ * Disables archive mode.
211
+ *
212
+ * @since 4.4.0
213
+ */
214
+ public static function disable_archive_mode() {
215
+ self::$archive_mode = false;
216
+ }
217
  }
classes/Connector/ConnectorInterface.php CHANGED
@@ -41,7 +41,9 @@ interface WSAL_Connector_ConnectorInterface {
41
  public function closeConnection();
42
 
43
  /**
44
- * Is installed?
 
 
45
  */
46
  public function isInstalled();
47
 
@@ -66,4 +68,14 @@ interface WSAL_Connector_ConnectorInterface {
66
  * Uninstall all.
67
  */
68
  public function uninstallAll();
 
 
 
 
 
 
 
 
 
 
69
  }
41
  public function closeConnection();
42
 
43
  /**
44
+ * Checks if the necessary tables are available
45
+ *
46
+ * @return bool true|false
47
  */
48
  public function isInstalled();
49
 
68
  * Uninstall all.
69
  */
70
  public function uninstallAll();
71
+
72
+ /**
73
+ * Run any query.
74
+ *
75
+ * @param string $query
76
+ *
77
+ * @return mixed
78
+ * @since 4.4.0
79
+ */
80
+ public function query( $query );
81
  }
classes/Connector/MySQLDB.php CHANGED
@@ -186,11 +186,7 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
186
  }
187
 
188
  /**
189
- * Gets an adapter for the specified model.
190
- *
191
- * @param string $class_name - Class name.
192
- *
193
- * @return WSAL_Adapters_ActiveRecordInterface
194
  */
195
  public function getAdapter( $class_name ) {
196
  $obj_name = $this->getAdapterClassName( $class_name );
@@ -210,9 +206,7 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
210
  }
211
 
212
  /**
213
- * Checks if the necessary tables are available
214
- *
215
- * @return bool true|false
216
  */
217
  public function isInstalled() {
218
  $wpdb = $this->getConnection();
@@ -228,7 +222,7 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
228
  * @param bool $is_external_database If true, some tables will not be created.
229
  */
230
  public function installAll( $is_external_database = false ) {
231
- $adapter_list = glob( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . '*.php' );
232
  $adapter_list = apply_filters( 'wsal_install_adapters_list', $adapter_list );
233
  foreach ( $adapter_list as $file ) {
234
  $file_path = explode( DIRECTORY_SEPARATOR, $file );
@@ -269,7 +263,7 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
269
  * Uninstall all DB tables.
270
  */
271
  public function uninstallAll() {
272
- foreach ( glob( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . '*.php' ) as $file ) {
273
  $file_path = explode( DIRECTORY_SEPARATOR, $file );
274
  $file_name = $file_path[ count( $file_path ) - 1 ];
275
  $class_name = $this->getAdapterClassName( str_replace( 'Adapter.php', '', $file_name ) );
@@ -323,7 +317,7 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
323
  return 0;
324
  }
325
 
326
- // insert data to the target database
327
  $occurrence_adapter_target = new WSAL_Adapters_MySQL_Occurrence( $target_db );
328
  $occurrence_table_name_target = $occurrence_adapter_target->GetTable();
329
 
@@ -334,8 +328,19 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
334
  'site_id' => $entry['site_id'],
335
  'alert_id' => $entry['alert_id'],
336
  'created_on' => $entry['created_on'],
337
- 'is_read' => $entry['is_read']
338
- ], [ '%d', '%d', '%f', '%d' ] );
 
 
 
 
 
 
 
 
 
 
 
339
 
340
  $old_entry_id = intval( $entry['id'] );
341
  $new_entry_id = $target_db->insert_id;
@@ -416,7 +421,6 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
416
  */
417
  public function MigrateOccurrenceFromExternalToLocal( $limit ) {
418
  global $wpdb;
419
-
420
  return $this->MigrateOccurrence( $this->getConnection(), $wpdb, $limit );
421
  }
422
 
@@ -493,18 +497,32 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
493
 
494
  $occurrence_new = new WSAL_Adapters_MySQL_Occurrence( $archive_db );
495
 
496
- $sql = 'INSERT INTO ' . $occurrence_new->GetTable() . ' (id, site_id, alert_id, created_on, is_read) VALUES ';
497
  foreach ( $occurrences as $entry ) {
498
- $sql .= $archive_db->prepare(
499
- '( %d, %d, %d, %d, %d ), ',
 
500
  intval( $entry['id'] ),
501
  intval( $entry['site_id'] ),
502
  intval( $entry['alert_id'] ),
503
  $entry['created_on'],
504
- 0
 
 
 
 
 
 
 
 
 
 
 
505
  );
506
- $args['occurrence_ids'][] = $entry['id'];
 
507
  }
 
508
  $sql = rtrim( $sql, ', ' );
509
  $archive_db->query( $sql );
510
 
@@ -599,4 +617,11 @@ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements
599
  // If both queries are successful, then return true.
600
  return $query_occ && $query_meta;
601
  }
 
 
 
 
 
 
 
602
  }
186
  }
187
 
188
  /**
189
+ * @inheritDoc
 
 
 
 
190
  */
191
  public function getAdapter( $class_name ) {
192
  $obj_name = $this->getAdapterClassName( $class_name );
206
  }
207
 
208
  /**
209
+ * @inheritDoc
 
 
210
  */
211
  public function isInstalled() {
212
  $wpdb = $this->getConnection();
222
  * @param bool $is_external_database If true, some tables will not be created.
223
  */
224
  public function installAll( $is_external_database = false ) {
225
+ $adapter_list = WSAL_Utilities_FileSystemUtils::read_files_in_folder( $this->getAdaptersDirectory(), '*.php' );
226
  $adapter_list = apply_filters( 'wsal_install_adapters_list', $adapter_list );
227
  foreach ( $adapter_list as $file ) {
228
  $file_path = explode( DIRECTORY_SEPARATOR, $file );
263
  * Uninstall all DB tables.
264
  */
265
  public function uninstallAll() {
266
+ foreach ( WSAL_Utilities_FileSystemUtils::read_files_in_folder( $this->getAdaptersDirectory(), '*.php' ) as $file ) {
267
  $file_path = explode( DIRECTORY_SEPARATOR, $file );
268
  $file_name = $file_path[ count( $file_path ) - 1 ];
269
  $class_name = $this->getAdapterClassName( str_replace( 'Adapter.php', '', $file_name ) );
317
  return 0;
318
  }
319
 
320
+ // Insert data to the target database.
321
  $occurrence_adapter_target = new WSAL_Adapters_MySQL_Occurrence( $target_db );
322
  $occurrence_table_name_target = $occurrence_adapter_target->GetTable();
323
 
328
  'site_id' => $entry['site_id'],
329
  'alert_id' => $entry['alert_id'],
330
  'created_on' => $entry['created_on'],
331
+ 'client_ip' => $entry['client_ip'],
332
+ 'severity' => $entry['severity'],
333
+ 'object' => $entry['object'],
334
+ 'event_type' => $entry['event_type'],
335
+ 'user_agent' => $entry['user_agent'],
336
+ 'user_roles' => $entry['user_roles'],
337
+ 'username' => $entry['username'],
338
+ 'user_id' => $entry['user_id'],
339
+ 'session_id' => $entry['session_id'],
340
+ 'post_status' => $entry['post_status'],
341
+ 'post_type' => $entry['post_type'],
342
+ 'post_id' => $entry['post_id'],
343
+ ], [ '%d', '%d', '%f','%s', '%d','%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%d'] );
344
 
345
  $old_entry_id = intval( $entry['id'] );
346
  $new_entry_id = $target_db->insert_id;
421
  */
422
  public function MigrateOccurrenceFromExternalToLocal( $limit ) {
423
  global $wpdb;
 
424
  return $this->MigrateOccurrence( $this->getConnection(), $wpdb, $limit );
425
  }
426
 
497
 
498
  $occurrence_new = new WSAL_Adapters_MySQL_Occurrence( $archive_db );
499
 
500
+ $sql = 'INSERT INTO ' . $occurrence_new->GetTable() . ' ( id, site_id, alert_id, created_on, client_ip, severity, object, event_type, user_agent, user_roles, username, user_id, session_id, post_status, post_type, post_id ) VALUES ';
501
  foreach ( $occurrences as $entry ) {
502
+
503
+ $sql .= $archive_db->prepare(
504
+ '( %d, %d, %d, %f, %s, %d, %s, %s, %s, %s, %s, %d, %s, %s, %s, %d ), ',
505
  intval( $entry['id'] ),
506
  intval( $entry['site_id'] ),
507
  intval( $entry['alert_id'] ),
508
  $entry['created_on'],
509
+ $entry['client_ip'],
510
+ $entry['severity'],
511
+ $entry['object'],
512
+ $entry['event_type'],
513
+ $entry['user_agent'],
514
+ $entry['user_roles'],
515
+ $entry['username'],
516
+ intval( $entry['user_id'] ),
517
+ $entry['session_id'],
518
+ $entry['post_status'],
519
+ $entry['post_type'],
520
+ intval( $entry['post_id'] )
521
  );
522
+
523
+ $args['occurrence_ids'][] = intval( $entry['id'] );
524
  }
525
+
526
  $sql = rtrim( $sql, ', ' );
527
  $archive_db->query( $sql );
528
 
617
  // If both queries are successful, then return true.
618
  return $query_occ && $query_meta;
619
  }
620
+
621
+ /**
622
+ * @inheritDoc
623
+ */
624
+ public function query( $query ) {
625
+ return $this->getConnection()->query( $query );
626
+ }
627
  }
classes/Helpers/Options.php CHANGED
@@ -94,26 +94,28 @@ class Options {
94
  * @param bool $autoload Whether or not to autoload this option.
95
  * @return bool Whether or not the option was updated.
96
  */
97
- public function set_option_value( $option_name = '', $value = null, $autoload = true ) {
98
  // bail early if no option name or value was passed.
99
  if ( empty( $option_name ) || null === $value ) {
100
  return;
101
  }
102
 
103
  $actual_option_name = $option_name;
104
- if (!preg_match( '/\A' .preg_quote($this->prefix) . '/', $option_name)) {
105
  // prepend prefix if not already present
106
  $actual_option_name = $this->prefix . $option_name;
107
  }
108
 
109
- return self::_set_option_value($actual_option_name, $value, $autoload);
110
  }
111
 
112
  /**
113
  * Deletes a plugin option from the WP options table.
114
  *
 
 
115
  * @since 4.0.2
116
- * @param string $option_name Name of the option to delete (including the prefix).
117
  * @return bool
118
  */
119
  public function delete_option( $option_name = '' ) {
@@ -202,8 +204,8 @@ class Options {
202
  * @param bool $autoload Whether or not to autoload this option.
203
  * @return mixed
204
  */
205
- public static function set_option_value_ignore_prefix( $option_name = '', $value = null, $autoload = true ) {
206
- return self::_set_option_value( $option_name, $value, $autoload = true );
207
  }
208
 
209
  /**
@@ -216,19 +218,23 @@ class Options {
216
  * @param bool $autoload Whether or not to autoload this option.
217
  * @return bool Whether or not the option was updated.
218
  */
219
- private static function _set_option_value( $option_name = '', $value = null, $autoload = true ) {
220
  // bail early if no option name or value was passed.
221
  if ( empty( $option_name ) || null === $value ) {
222
  return;
223
  }
224
 
225
- if (is_multisite()) {
226
- switch_to_blog(get_main_network_id());
 
 
 
 
227
  }
228
 
229
  $result = \update_option( $option_name, $value, $autoload );
230
 
231
- if (is_multisite()) {
232
  restore_current_blog();
233
  }
234
 
@@ -260,23 +266,4 @@ class Options {
260
  return true === $bool ? 'yes' : 'no';
261
  }
262
 
263
- /**
264
- * Create neat email/sms string to display in the event.
265
- *
266
- * @param string $email
267
- * @param string $sms
268
- * @return string
269
- */
270
- public static function create_recipient_string( $email, $sms ) {
271
- $recipient = ( isset( $email ) ) ? $email : '';
272
- if ( isset( $sms ) && ! empty( $sms ) ) {
273
- // Only add seperator if needed.
274
- if ( ! empty( $recipient ) ) {
275
- $recipient .= ' | ';
276
- }
277
- $recipient .= $sms;
278
- }
279
-
280
- return $recipient;
281
- }
282
  }
94
  * @param bool $autoload Whether or not to autoload this option.
95
  * @return bool Whether or not the option was updated.
96
  */
97
+ public function set_option_value( $option_name = '', $value = null, $autoload = false ) {
98
  // bail early if no option name or value was passed.
99
  if ( empty( $option_name ) || null === $value ) {
100
  return;
101
  }
102
 
103
  $actual_option_name = $option_name;
104
+ if ( ! preg_match( '/\A' . preg_quote( $this->prefix ) . '/', $option_name ) ) {
105
  // prepend prefix if not already present
106
  $actual_option_name = $this->prefix . $option_name;
107
  }
108
 
109
+ return self::_set_option_value( $actual_option_name, $value, $autoload );
110
  }
111
 
112
  /**
113
  * Deletes a plugin option from the WP options table.
114
  *
115
+ * Hanled option name with and without the prefix for backwards compatibility.
116
+ *
117
  * @since 4.0.2
118
+ * @param string $option_name Name of the option to delete.
119
  * @return bool
120
  */
121
  public function delete_option( $option_name = '' ) {
204
  * @param bool $autoload Whether or not to autoload this option.
205
  * @return mixed
206
  */
207
+ public static function set_option_value_ignore_prefix( $option_name = '', $value = null, $autoload = false ) {
208
+ return self::_set_option_value( $option_name, $value, $autoload );
209
  }
210
 
211
  /**
218
  * @param bool $autoload Whether or not to autoload this option.
219
  * @return bool Whether or not the option was updated.
220
  */
221
+ private static function _set_option_value( $option_name = '', $value = null, $autoload = false ) {
222
  // bail early if no option name or value was passed.
223
  if ( empty( $option_name ) || null === $value ) {
224
  return;
225
  }
226
 
227
+ if ( is_multisite() ) {
228
+ switch_to_blog( get_main_network_id() );
229
+ }
230
+
231
+ if ( false === $autoload ) {
232
+ delete_option( $option_name );
233
  }
234
 
235
  $result = \update_option( $option_name, $value, $autoload );
236
 
237
+ if ( is_multisite() ) {
238
  restore_current_blog();
239
  }
240
 
266
  return true === $bool ? 'yes' : 'no';
267
  }
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  }
classes/Loggers/Database.php CHANGED
@@ -16,10 +16,10 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * Loggers Class.
18
  *
19
- * This class store the logs in the Database and adds the promo
20
- * alerts, there is also the function to clean up alerts.
21
  *
22
  * @package wsal
 
23
  */
24
  class WSAL_Loggers_Database extends WSAL_AbstractLogger {
25
 
@@ -154,63 +154,4 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
154
  // Notify system.
155
  do_action( 'wsal_prune', $deleted_count, vsprintf( $result['sql'], $result['args'] ) );
156
  }
157
-
158
- /**
159
- * Get the promo id, to send each time a different promo,
160
- * keeping the last id saved in the DB.
161
- *
162
- * @return integer $promoToSend - The array index.
163
- */
164
- private function GetPromoAlert() {
165
- $last_promo_sent_id = $this->plugin->GetGlobalSetting( 'promo-send-id' );
166
- $last_promo_sent_id = empty( $last_promo_sent_id ) ? 0 : $last_promo_sent_id;
167
- $promo_to_send = null;
168
- $promo_alerts = $this->GetActivePromoText();
169
- if ( ! empty( $promo_alerts ) ) {
170
- $promo_to_send = isset( $promo_alerts[ $last_promo_sent_id ] ) ? $promo_alerts[ $last_promo_sent_id ] : $promo_alerts[0];
171
-
172
- if ( $last_promo_sent_id < count( $promo_alerts ) - 1 ) {
173
- $last_promo_sent_id++;
174
- } else {
175
- $last_promo_sent_id = 0;
176
- }
177
- $this->plugin->SetGlobalSetting( 'promo-send-id', $last_promo_sent_id );
178
- }
179
- return $promo_to_send;
180
- }
181
-
182
- /**
183
- * Array of promo.
184
- *
185
- * @return array $promo_alerts - The array of promo.
186
- */
187
- private function GetActivePromoText() {
188
- $promo_alerts = array();
189
- $promo_alerts[] = array(
190
- 'name' => 'Upgrade to Premium',
191
- 'message' => 'See who is logged in, create user productivity reports, get notified instantly via email of important changes, add search and much more. <strong>%1$s</strong> | <strong>%2$s</strong>',
192
- );
193
- $promo_alerts[] = array(
194
- 'name' => 'See Who is Logged In, receive Email Alerts, generate User Productivity Reports and more!',
195
- 'message' => 'Upgrade to premium and extend the plugin’s features with email alerts, reports tool, free-text based search, user logins and sessions management and more! <strong>%1$s</strong> | <strong>%2$s</strong>',
196
- );
197
- return $promo_alerts;
198
- }
199
-
200
- /**
201
- * Check condition to show promo.
202
- *
203
- * @return integer|null - Counter alert.
204
- */
205
- private function CheckPromoToShow() {
206
- // If the package is free, show the promo.
207
- if ( ! class_exists( 'WSAL_NP_Plugin' )
208
- && ! class_exists( 'WSAL_Ext_Plugin' )
209
- && ! class_exists( 'WSAL_Rep_Plugin' )
210
- && ! class_exists( 'WSAL_SearchExtension' )
211
- && ! class_exists( 'WSAL_UserSessions_Plugin' ) ) {
212
- return 150;
213
- }
214
- return null;
215
- }
216
  }
16
  /**
17
  * Loggers Class.
18
  *
19
+ * This class stores the logs in the database and there is also the function to clean up alerts.
 
20
  *
21
  * @package wsal
22
+ * @subpackage loggers
23
  */
24
  class WSAL_Loggers_Database extends WSAL_AbstractLogger {
25
 
154
  // Notify system.
155
  do_action( 'wsal_prune', $deleted_count, vsprintf( $result['sql'], $result['args'] ) );
156
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
  }
classes/MainWpApi.php ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Handler for MainWP API endpoints.
5
+ *
6
+ * @package wsal
7
+ * @subpackage main-wp
8
+ * @since 4.4.0
9
+ */
10
+ class WSAL_MainWpApi {
11
+
12
+ /**
13
+ * Instance of WpSecurityAuditLog.
14
+ *
15
+ * @var WpSecurityAuditLog
16
+ */
17
+ protected $plugin;
18
+
19
+ /**
20
+ * Method: Constructor.
21
+ *
22
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
23
+ */
24
+ public function __construct( WpSecurityAuditLog $plugin ) {
25
+ $this->plugin = $plugin;
26
+ }
27
+
28
+ /**
29
+ * MainWP API Handler.
30
+ *
31
+ * @param array $info – Information to return.
32
+ * @param array $post_data – Post data array from MainWP.
33
+ *
34
+ * @return mixed
35
+ * @since 3.2.5
36
+ */
37
+ public function handle_callback( $info, $post_data ) {
38
+ if ( isset( $post_data['action'] ) ) {
39
+ switch ( $post_data['action'] ) {
40
+ case 'check_wsal':
41
+ return $this->handle_wsal_info_check();
42
+
43
+ case 'get_events':
44
+ $limit = isset( $post_data['events_count'] ) ? $post_data['events_count'] : false;
45
+ $offset = isset( $post_data['events_offset'] ) ? $post_data['events_offset'] : false;
46
+ $query_args = isset( $post_data['query_args'] ) ? $post_data['query_args'] : false;
47
+ return $this->get_events_data( $limit, $offset, $query_args );
48
+
49
+ case 'get_report':
50
+ $filters = isset( $post_data['filters'] ) ? $post_data['filters'] : array();
51
+ $report_type = isset( $post_data['report_type'] ) ? $post_data['report_type'] : false;
52
+ return $this->get_report_data( $filters, $report_type );
53
+
54
+ case 'latest_event':
55
+ // run the query and return it.
56
+ $event = $this->query_for_latest_event();
57
+ $event = $event->getAdapter()->Execute( $event );
58
+
59
+ // Set the return object.
60
+ if ( isset( $event[0] ) ) {
61
+ $info = new stdClass();
62
+ $info->alert_id = $event[0]->alert_id;
63
+ $info->created_on = $event[0]->created_on;
64
+ } else {
65
+ $info = false;
66
+ }
67
+ break;
68
+ case 'enforce_settings':
69
+ return $this->handle_settings_enforcement( $post_data );
70
+
71
+ case 'get_event_definitions':
72
+ return $this->get_event_definitions();
73
+
74
+ default:
75
+ break;
76
+ }
77
+ }
78
+
79
+ return $info;
80
+ }
81
+
82
+ /**
83
+ * Handles API call requesting info about WSAL plugin.
84
+ *
85
+ * @return stdClass
86
+ * @since 4.4.0
87
+ */
88
+ protected function handle_wsal_info_check() {
89
+ $info = new stdClass();
90
+ $info->wsal_installed = true;
91
+ $info->is_premium = false;
92
+ return $info;
93
+ }
94
+
95
+ /**
96
+ * Return alerts for MainWP Extension.
97
+ *
98
+ * @param integer $limit - Number of alerts to retrieve.
99
+ * @param int|bool $offset - Events offset, otherwise false.
100
+ * @param stdClass|bool $query_args - Events query arguments, otherwise false.
101
+ *
102
+ * @return stdClass
103
+ */
104
+ public function get_events_data( $limit = 100, $offset = false, $query_args = false ) {
105
+ $mwp_events = new stdClass();
106
+
107
+ // Check if limit is not empty.
108
+ if ( empty( $limit ) ) {
109
+ return $mwp_events;
110
+ }
111
+
112
+ // Initiate query occurrence object.
113
+ $events_query = new WSAL_Models_OccurrenceQuery();
114
+ $events_query->addCondition( 'site_id = %s ', 1 ); // Set site id.
115
+ $events_query = $this->filter_query( $events_query, $query_args );
116
+
117
+ // Check query arguments.
118
+ if ( false !== $query_args ) {
119
+ if ( isset( $query_args['get_count'] ) && $query_args['get_count'] ) {
120
+ $mwp_events->total_items = $events_query->getAdapter()->Count( $events_query );
121
+ } else {
122
+ $mwp_events->total_items = false;
123
+ }
124
+ }
125
+
126
+ // Set order by.
127
+ $events_query->addOrderBy( 'created_on', true );
128
+
129
+ // Set the limit.
130
+ $events_query->setLimit( $limit );
131
+
132
+ // Set the offset.
133
+ if ( false !== $offset ) {
134
+ $events_query->setOffset( $offset );
135
+ }
136
+
137
+ // Execute the query.
138
+ /** @var \WSAL\MainWPExtension\Models\Occurrence[] $events */
139
+ $events = $events_query->getAdapter()->Execute( $events_query );
140
+
141
+ if ( ! empty( $events ) && is_array( $events ) ) {
142
+ foreach ( $events as $event ) {
143
+ // Get event meta.
144
+ $meta_data = $event->GetMetaArray();
145
+ $meta_data['UserData'] = $this->plugin->alerts->get_event_user_data( WSAL_Utilities_UsersUtils::GetUsername( $meta_data ) );
146
+ $mwp_events->events[ $event->id ] = new stdClass();
147
+ $mwp_events->events[ $event->id ]->id = $event->id;
148
+ $mwp_events->events[ $event->id ]->alert_id = $event->alert_id;
149
+ $mwp_events->events[ $event->id ]->created_on = $event->created_on;
150
+ $mwp_events->events[ $event->id ]->meta_data = $meta_data;
151
+ }
152
+
153
+ $mwp_events->users = $this->plugin->alerts->get_wp_users();
154
+ }
155
+
156
+ return $mwp_events;
157
+ }
158
+
159
+ /**
160
+ * Filter query for MWPAL.
161
+ *
162
+ * @param WSAL_Models_OccurrenceQuery $query Events query.
163
+ * @param array $query_args Query args.
164
+ *
165
+ * @return WSAL_Models_OccurrenceQuery
166
+ */
167
+ private function filter_query( $query, $query_args ) {
168
+ if ( isset( $query_args['search_term'] ) && $query_args['search_term'] ) {
169
+ $query->addSearchCondition( $query_args['search_term'] );
170
+ }
171
+
172
+ if ( ! empty( $query_args['search_filters'] ) ) {
173
+ // Get DB connection array.
174
+ $connection = $this->plugin->getConnector()->getAdapter( 'Occurrence' )->get_connection();
175
+
176
+ // Tables.
177
+ $meta = new WSAL_Adapters_MySQL_Meta( $connection );
178
+ $table_meta = $meta->GetTable(); // Metadata.
179
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $connection );
180
+ $table_occ = $occurrence->GetTable(); // Occurrences.
181
+
182
+ foreach ( $query_args['search_filters'] as $prefix => $value ) {
183
+ if ( 'event' === $prefix ) {
184
+ $query->addORCondition( array( 'alert_id = %s' => $value ) );
185
+ } elseif ( in_array( $prefix, array( 'from', 'to', 'on' ), true ) ) {
186
+ $date = DateTime::createFromFormat( $this->sanitized_date_format, $value[0] );
187
+ $date->setTime( 0, 0 ); // Reset time to 00:00:00.
188
+ $date_string = $date->format( 'U' );
189
+
190
+ if ( 'from' === $prefix ) {
191
+ $query->addCondition( 'created_on >= %s', $date_string );
192
+ } elseif ( 'to' === $prefix ) {
193
+ $query->addCondition( 'created_on <= %s', strtotime( '+1 day -1 minute', $date_string ) );
194
+ } elseif ( 'on' === $prefix ) {
195
+ $query->addCondition( 'created_on >= %s', strtotime( '-1 day +1 day +1 second', $date_string ) );
196
+ $query->addCondition( 'created_on <= %s', strtotime( '+1 day -1 second', $date_string ) );
197
+ }
198
+ } elseif ( in_array( $prefix, array( 'username', 'firstname', 'lastname' ), true ) ) {
199
+ $users = array();
200
+ if ( 'username' === $prefix ) {
201
+ foreach ( $value as $username ) {
202
+ $user = get_user_by( 'login', $username );
203
+ if ( ! $user ) {
204
+ $user = get_user_by( 'slug', $username );
205
+ }
206
+
207
+ if ( $user ) {
208
+ array_push( $users, $user );
209
+ }
210
+ }
211
+ } elseif ( 'firstname' === $prefix || 'lastname' === $prefix ) {
212
+
213
+ $meta_key = 'firstname' === $prefix ? 'first_name' : ( 'lastname' === $prefix ? 'last_name' : false );
214
+
215
+ foreach ( $value as $name ) {
216
+ $users_array = get_users(
217
+ array(
218
+ 'meta_key' => $meta_key,
219
+ 'meta_value' => $name,
220
+ 'fields' => array( 'ID', 'user_login' ),
221
+ 'meta_compare' => 'LIKE',
222
+ )
223
+ );
224
+
225
+ foreach ( $users_array as $user ) {
226
+ array_push( $users, $user );
227
+ }
228
+ }
229
+ }
230
+
231
+ if ( ! empty( $users ) ) {
232
+ global $wpdb;
233
+ $usernames = wp_list_pluck( $users, 'user_login' );
234
+ $placeholders_string = implode( ', ', array_fill( 0, count( $usernames ), '%s' ) );
235
+
236
+ $sql = $wpdb->prepare( 'username IN ( ' . $placeholders_string . ' ) ', $usernames );
237
+
238
+ $user_ids = wp_list_pluck( $users, 'ID' );
239
+ $placeholders_string = implode( ', ', array_fill( 0, count( $user_ids ), '%d' ) );
240
+
241
+ $sql .= ' OR ' . $wpdb->prepare( 'user_id IN ( ' . $placeholders_string . ' ) ', $user_ids );
242
+ $query->addORCondition( array( $sql => '' ) );
243
+ }
244
+ } elseif ( 'userrole' === $prefix ) {
245
+ // User role search condition.
246
+ $sql = "$table_occ.user_role replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP %s )";
247
+ $value = implode( '|', $value );
248
+ $query->addORCondition( array( $sql => $value ) );
249
+ } elseif ( 'postname' === $prefix ) {
250
+
251
+ $sql = "$table_occ.id IN ( SELECT occurrence_id FROM $table_meta as meta WHERE meta.name='PostTitle' AND ( ";
252
+ $value = array_map( array( $this, 'add_string_wildcards' ), $value );
253
+
254
+ // Get the last value.
255
+ $last_value = end( $value );
256
+
257
+ foreach ( $value as $column_name ) {
258
+ if ( $last_value === $column_name ) {
259
+ continue;
260
+ }
261
+ $sql .= "( (meta.value LIKE '$column_name') > 0 ) OR ";
262
+ }
263
+
264
+ // Add placeholder for the last value.
265
+ $sql .= "( (meta.value LIKE '%s') > 0 ) ) )";
266
+
267
+ $query->addORCondition( array( $sql => $last_value ) );
268
+ } elseif ( in_array( $prefix, array( 'posttype', 'poststatus', 'postid' ), true ) ) {
269
+ $column_name = '';
270
+ if ( 'posttype' === $prefix ) {
271
+ $column_name = 'post_type';
272
+ } elseif ( 'poststatus' === $prefix ) {
273
+ $column_name = 'post_status';
274
+ } elseif ( 'postid' === $prefix ) {
275
+ $column_name = 'post_id';
276
+ }
277
+
278
+ $sql = " {$column_name} = %s ";
279
+ $query->addORCondition( array( $sql => $value ) );
280
+ } elseif ( 'ip' === $prefix ) {
281
+ // IP search condition.
282
+ $sql = "$table_occ.client_ip = %s ";
283
+ $query->addORCondition( array( $sql => $value ) );
284
+ }
285
+ }
286
+ }
287
+
288
+ return $query;
289
+ }
290
+
291
+ /**
292
+ * Generate report matching the filter passed.
293
+ *
294
+ * @param array $filters - Filters.
295
+ * @param mixed $report_type - Type of report.
296
+ *
297
+ * @return stdClass
298
+ *
299
+ * @since 4.4.0 Removed support for report type "statistics_unique_ips".
300
+ */
301
+ public function get_report_data( array $filters, $report_type ) {
302
+ $report = new stdClass();
303
+ $report->data = array();
304
+
305
+ if ( 'statistics_unique_ips' === $report_type ) {
306
+ // Support for this report was removed in version 4.4.0, but we still returned empty dataset to avoid issues
307
+ // in the MainWP extension.
308
+ return $report;
309
+ }
310
+
311
+ do {
312
+ $response = $this->generate_report( $filters );
313
+
314
+ if ( isset( $response['data'] ) ) {
315
+ $report->data = array_merge( $report->data, $response['data'] );
316
+ }
317
+
318
+ // Set the filters next date.
319
+ $filters['nextDate'] = ( isset( $response['lastDate'] ) && $response['lastDate'] ) ? $response['lastDate'] : 0;
320
+ } while ( $filters['nextDate'] );
321
+
322
+ return $report;
323
+ }
324
+
325
+ /**
326
+ * Performs a query to retrieve the latest event in the logs.
327
+ *
328
+ * @return array
329
+ * @since 4.0.3
330
+ */
331
+ public function query_for_latest_event() {
332
+ $event_query = new WSAL_Models_OccurrenceQuery();
333
+ // order by creation.
334
+ $event_query->addOrderBy( 'created_on', true );
335
+ // only request 1 item.
336
+ $event_query->setLimit( 1 );
337
+
338
+ return $event_query;
339
+ }
340
+
341
+ /**
342
+ * Handles API call enforcing certain WSAL settings.
343
+ *
344
+ * @param array $post_data Received request data.
345
+ *
346
+ * @return string[]
347
+ * @since 4.4.0
348
+ */
349
+ private function handle_settings_enforcement( $post_data ) {
350
+ // Check subaction.
351
+ if ( ! array_key_exists( 'subaction', $post_data ) || empty( $post_data['subaction'] ) ) {
352
+ return array(
353
+ 'success' => 'no',
354
+ 'message' => 'Missing subaction parameter.',
355
+ );
356
+ }
357
+
358
+ $subaction = filter_var( $post_data['subaction'], FILTER_SANITIZE_STRING );
359
+ if ( ! in_array( $subaction, array( 'update', 'remove' ), true ) ) {
360
+ return array(
361
+ 'success' => 'no',
362
+ 'message' => 'Unsupported subaction parameter value.',
363
+ );
364
+ }
365
+
366
+ if ( 'update' === $subaction ) {
367
+ // Store the enforced settings in local database (used for example to disable related parts
368
+ // of the settings UI).
369
+ $settings_to_enforce = $post_data['settings'];
370
+ $this->plugin->settings()->set_mainwp_enforced_settings( $settings_to_enforce );
371
+
372
+ // Change the existing settings.
373
+ if ( array_key_exists( 'pruning_enabled', $settings_to_enforce ) ) {
374
+ $this->plugin->settings()->SetPruningDateEnabled( $settings_to_enforce['pruning_enabled'] );
375
+ if ( array_key_exists( 'pruning_date', $settings_to_enforce ) && array_key_exists( 'pruning_unit', $settings_to_enforce ) ) {
376
+ $this->plugin->settings()->SetPruningDate( $settings_to_enforce['pruning_date'] . ' ' . $settings_to_enforce['pruning_unit'] );
377
+ $this->plugin->settings()->set_pruning_unit( $settings_to_enforce['pruning_unit'] );
378
+ }
379
+ }
380
+
381
+ if ( array_key_exists( 'disabled_events', $settings_to_enforce ) ) {
382
+ $disabled_event_ids = array_map( 'intval', explode( ',', $settings_to_enforce['disabled_events'] ) );
383
+ $this->plugin->alerts->SetDisabledAlerts( $disabled_event_ids );
384
+ }
385
+
386
+ if ( array_key_exists( 'incognito_mode_enabled', $settings_to_enforce ) ) {
387
+ $this->plugin->settings()->SetIncognito( $settings_to_enforce['incognito_mode_enabled'] );
388
+ }
389
+
390
+ if ( array_key_exists( 'login_notification_enabled', $settings_to_enforce ) ) {
391
+ $login_page_notification_enabled = $settings_to_enforce['login_notification_enabled'];
392
+ $this->plugin->settings()->set_login_page_notification( $login_page_notification_enabled );
393
+ if ( 'yes' === $login_page_notification_enabled ) {
394
+ $this->plugin->settings()->set_login_page_notification_text( $settings_to_enforce['login_notification_text'] );
395
+ }
396
+ }
397
+ } elseif ( 'remove' === $subaction ) {
398
+ $this->plugin->settings()->delete_mainwp_enforced_settings();
399
+ }
400
+
401
+ $this->plugin->alerts->Trigger( 6043 );
402
+
403
+ return array(
404
+ 'success' => 'yes',
405
+ );
406
+ }
407
+
408
+ /**
409
+ * Generates report for MainWP extension.
410
+ *
411
+ * @param array $filters - Filters.
412
+ *
413
+ * @return array
414
+ */
415
+ private function generate_report( $filters ) {
416
+ // Check the report format.
417
+ $report_format = empty( $filters['report-format'] ) ? 'html' : $filters['report-format'];
418
+ if ( ! in_array( $report_format, array( 'csv', 'html' ), true ) ) {
419
+ $report_format = WSAL_Rep_DataFormat::get_default();
420
+ }
421
+
422
+ // Alert codes filter needs to renamed to work correctly with further report processing.
423
+ if ( array_key_exists( 'alert-codes', $filters ) ) {
424
+ $filters['alert_codes'] = $filters['alert-codes'];
425
+ unset( $filters['alert-codes'] );
426
+ }
427
+
428
+ $args = WSAL_ReportArgs::build_from_alternative_filters( $filters );
429
+ $next_date = empty( $filters['nextDate'] ) ? null : $filters['nextDate'];
430
+ $limit = empty( $filters['limit'] ) ? 0 : $filters['limit'];
431
+ $last_date = null;
432
+
433
+ $results = $this->plugin->getConnector()->getAdapter( 'Occurrence' )->get_report_data( $args, $next_date, $limit );
434
+
435
+ if ( ! empty( $results['lastDate'] ) ) {
436
+ $last_date = $results['lastDate'];
437
+ unset( $results['lastDate'] );
438
+ }
439
+
440
+ if ( empty( $results ) ) {
441
+ return false;
442
+ }
443
+
444
+ $data = array();
445
+
446
+ // Get alert details.
447
+ foreach ( $results as $entry ) {
448
+ if ( 9999 === (int) $entry->alert_id ) {
449
+ continue;
450
+ }
451
+
452
+ array_push( $data, $this->plugin->alerts->get_alert_details( $entry, 'report-' . $report_format ) );
453
+ }
454
+
455
+ if ( empty( $data ) ) {
456
+ return false;
457
+ }
458
+
459
+ return array(
460
+ 'data' => $data,
461
+ 'filters' => $filters,
462
+ 'lastDate' => $last_date,
463
+ );
464
+ }
465
+
466
+ /**
467
+ * Retrieves the events definitions along with some other related pieces of information, such as event object and
468
+ * event type labels.
469
+ *
470
+ * @return array Events definitions data.
471
+ *
472
+ * @since 4.4.0
473
+ */
474
+ private function get_event_definitions() {
475
+ $alert_manager = $this->plugin->alerts;
476
+
477
+ return array(
478
+ 'events' => $alert_manager->GetAlerts(),
479
+ 'objects' => $alert_manager->get_event_objects_data(),
480
+ 'types' => $alert_manager->get_event_type_data(),
481
+ );
482
+ }
483
+ }
classes/Models/ActiveRecord.php CHANGED
@@ -41,6 +41,15 @@ abstract class WSAL_Models_ActiveRecord {
41
  */
42
  protected $id = false;
43
 
 
 
 
 
 
 
 
 
 
44
  /**
45
  * Adapter Name.
46
  *
@@ -53,7 +62,7 @@ abstract class WSAL_Models_ActiveRecord {
53
  *
54
  * @var boolean
55
  */
56
- protected $useDefaultAdapter = false;
57
 
58
  /**
59
  * Record State.
@@ -129,7 +138,8 @@ abstract class WSAL_Models_ActiveRecord {
129
  if ( ! empty( $this->connector ) ) {
130
  return $this->connector;
131
  }
132
- if ( $this->useDefaultAdapter ) {
 
133
  $this->connector = WSAL_Connector_ConnectorFactory::GetDefaultConnector();
134
  } else {
135
  $this->connector = WSAL_Connector_ConnectorFactory::GetConnector();
@@ -141,14 +151,29 @@ abstract class WSAL_Models_ActiveRecord {
141
  * Gets an adapter for the specified model
142
  * based on the adapter name.
143
  *
144
- * @see WSAL_Connector_ConnectorInterface::getAdapter()
145
- *
146
  * @return WSAL_Adapters_ActiveRecordInterface
147
  */
148
  public function getAdapter() {
 
 
 
 
 
 
149
  return $this->getConnector()->getAdapter( $this->adapterName );
150
  }
151
 
 
 
 
 
 
 
 
 
 
 
 
152
  /**
153
  * Load record from DB.
154
  *
@@ -166,6 +191,37 @@ abstract class WSAL_Models_ActiveRecord {
166
  }
167
  }
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  /**
170
  * Load object data from variable.
171
  *
@@ -177,30 +233,7 @@ abstract class WSAL_Models_ActiveRecord {
177
  $copy = new $copy();
178
  foreach ( (array) $data as $key => $val ) {
179
  if ( isset( $copy->$key ) ) {
180
- switch ( true ) {
181
- case $this->is_ip_address( $val ):
182
- $this->$key = (string) $val;
183
- break;
184
- case is_array( $copy->$key ):
185
- case is_object( $copy->$key ):
186
- $json_decoded_val = WSAL_Helpers_DataHelper::JsonDecode( $val );
187
- $this->$key = ( null == $json_decoded_val ) ? $val : $json_decoded_val;
188
- break;
189
- case is_int( $copy->$key ):
190
- $this->$key = (int) $val;
191
- break;
192
- case is_float( $copy->$key ):
193
- $this->$key = (float) $val;
194
- break;
195
- case is_bool( $copy->$key ):
196
- $this->$key = (bool) $val;
197
- break;
198
- case is_string( $copy->$key ):
199
- $this->$key = (string) $val;
200
- break;
201
- default:
202
- throw new Exception( 'Unsupported type "' . gettype( $copy->$key ) . '"' );
203
- }
204
  }
205
  }
206
  return $this;
@@ -357,27 +390,4 @@ abstract class WSAL_Models_ActiveRecord {
357
  protected static function CacheClear() {
358
  self::$_cache = array();
359
  }
360
-
361
- /**
362
- * Function used in WSAL reporting extension.
363
- *
364
- * @param WSAL_ReportArgs $report_args
365
- *
366
- * @return array Report results.
367
- * @see WSAL_Adapters_MySQL_ActiveRecord::GetReporting()
368
- */
369
- public function GetReporting( $report_args ) {
370
- return $this->getAdapter()->GetReporting( $report_args );
371
- }
372
-
373
- /**
374
- * Check if the float is IPv4 instead.
375
- *
376
- * @see WSAL_Models_ActiveRecord::LoadData()
377
- * @param float $ip_address - Number to check.
378
- * @return bool result validation
379
- */
380
- private function is_ip_address( $ip_address ) {
381
- return filter_var( $ip_address, FILTER_VALIDATE_IP ) !== false;
382
- }
383
  }
41
  */
42
  protected $id = false;
43
 
44
+ /**
45
+ * This property is used in conjunction with its setter method to force an adapter used by this record. It completely
46
+ * bypasses the default way of creating the adapter using its name and a connector returned by function getConnector.
47
+ *
48
+ * @var WSAL_Adapters_ActiveRecordInterface
49
+ * @since 4.4.0
50
+ */
51
+ protected $adapter = null;
52
+
53
  /**
54
  * Adapter Name.
55
  *
62
  *
63
  * @var boolean
64
  */
65
+ protected $use_default_adapter = false;
66
 
67
  /**
68
  * Record State.
138
  if ( ! empty( $this->connector ) ) {
139
  return $this->connector;
140
  }
141
+
142
+ if ( $this->use_default_adapter ) {
143
  $this->connector = WSAL_Connector_ConnectorFactory::GetDefaultConnector();
144
  } else {
145
  $this->connector = WSAL_Connector_ConnectorFactory::GetConnector();
151
  * Gets an adapter for the specified model
152
  * based on the adapter name.
153
  *
 
 
154
  * @return WSAL_Adapters_ActiveRecordInterface
155
  */
156
  public function getAdapter() {
157
+ // use forcefully set adapter if set
158
+ if ( ! empty( $this->adapter ) ) {
159
+ return $this->adapter;
160
+ }
161
+
162
+ // create adapter using the connector returned from getConnector method
163
  return $this->getConnector()->getAdapter( $this->adapterName );
164
  }
165
 
166
+ /**
167
+ * Allows the database adapter to be set from the outside. This is useful during data migrations.
168
+ *
169
+ * @param WSAL_Adapters_ActiveRecordInterface $adapter
170
+ *
171
+ * @since 4.4.0
172
+ */
173
+ public function setAdapter( $adapter ) {
174
+ $this->adapter = $adapter;
175
+ }
176
+
177
  /**
178
  * Load record from DB.
179
  *
191
  }
192
  }
193
 
194
+ /**
195
+ * Casts given value to a correct type based on the type of property (identified by the $key) in the $copy object.
196
+ * This is to allow automatic type casting instead of handling each database column individually.
197
+ *
198
+ * @param object $copy
199
+ * @param string $key
200
+ * @param mixed $val
201
+ *
202
+ * @return mixed
203
+ * @throws Exception
204
+ */
205
+ protected function cast_to_correct_type( $copy, $key, $val ){
206
+ switch ( true ) {
207
+ case is_string( $copy->$key ):
208
+ case WSAL_Utilities_RequestUtils::is_ip_address( $val ):
209
+ return (string) $val;
210
+ case is_array( $copy->$key ):
211
+ case is_object( $copy->$key ):
212
+ $json_decoded_val = WSAL_Helpers_DataHelper::JsonDecode( $val );
213
+ return is_null( $json_decoded_val ) ? $val : $json_decoded_val;
214
+ case is_int( $copy->$key ):
215
+ return (int) $val;
216
+ case is_float( $copy->$key ):
217
+ return (float) $val;
218
+ case is_bool( $copy->$key ):
219
+ return (bool) $val;
220
+ default:
221
+ throw new Exception( 'Unsupported type "' . gettype( $copy->$key ) . '"' );
222
+ }
223
+ }
224
+
225
  /**
226
  * Load object data from variable.
227
  *
233
  $copy = new $copy();
234
  foreach ( (array) $data as $key => $val ) {
235
  if ( isset( $copy->$key ) ) {
236
+ $this->$key = $this->cast_to_correct_type($copy, $key, $val);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  }
238
  }
239
  return $this;
390
  protected static function CacheClear() {
391
  self::$_cache = array();
392
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  }
classes/Models/Meta.php CHANGED
@@ -80,9 +80,9 @@ class WSAL_Models_Meta extends WSAL_Models_ActiveRecord {
80
  * @param mixed $value - Meta value.
81
  * @param integer $occurrence_id - Occurrence_id.
82
  *
83
- *@see WSAL_Adapters_MySQL_Meta::LoadByNameAndOccurrenceId()
84
  */
85
- public function UpdateByNameAndOccurenceId( $name, $value, $occurrence_id ) {
86
  $meta = $this->getAdapter()->LoadByNameAndOccurrenceId( $name, $occurrence_id );
87
  if ( ! empty( $meta ) ) {
88
  $this->id = $meta['id'];
80
  * @param mixed $value - Meta value.
81
  * @param integer $occurrence_id - Occurrence_id.
82
  *
83
+ * @see WSAL_Adapters_MySQL_Meta::LoadByNameAndOccurrenceId()
84
  */
85
+ public function UpdateByNameAndOccurrenceId( $name, $value, $occurrence_id ) {
86
  $meta = $this->getAdapter()->LoadByNameAndOccurrenceId( $name, $occurrence_id );
87
  if ( ! empty( $meta ) ) {
88
  $this->id = $meta['id'];
classes/Models/Occurrence.php CHANGED
@@ -21,6 +21,21 @@ if ( ! defined( 'ABSPATH' ) ) {
21
  */
22
  class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * Occurrence ID.
26
  *
@@ -50,20 +65,100 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
50
  public $created_on = 0.0;
51
 
52
  /**
53
- * Is read.
 
 
 
 
 
 
 
 
54
  *
55
- * @var bool
56
- * @deprecated 4.3.2
57
  */
58
- public $is_read = false;
59
 
60
  /**
61
- * Is migrated.
62
  *
63
- * @var bool
64
- * @deprecated 4.3.2
 
 
 
 
 
 
 
 
65
  */
66
- public $is_migrated = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  /**
69
  * Model Name.
@@ -103,50 +198,84 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
103
  * @see WSAL_Adapters_MySQL_Occurrence::GetNamedMeta()
104
  */
105
  public function GetMetaValue( $name, $default = array() ) {
106
- // Get meta adapter.
107
- $meta = $this->getAdapter()->GetNamedMeta( $this, $name );
108
- if ( is_null( $meta ) || ! array_key_exists( 'value', $meta ) ) {
109
- return $default;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
 
112
- return maybe_unserialize( $meta['value'] );
113
  }
114
 
115
  /**
116
  * Sets the value of a meta item (creates or updates meta item).
117
  *
118
  * @param string $name - Meta name.
119
- * @param mixed $value - Meta value.
120
  */
121
  public function SetMetaValue( $name, $value ) {
122
  // check explicitly for `0` string values.
123
  if ( '0' === $value || ! empty( $value ) ) {
124
- // Get meta adapter.
125
- $model = new WSAL_Models_Meta();
126
- $model->occurrence_id = $this->getId();
127
- $model->name = $name;
128
- $model->value = maybe_serialize( $value );
129
- $model->SaveMeta();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  }
131
  }
132
 
133
  /**
134
  * Update Metadata of this occurrence by name.
135
  *
136
- * @see WSAL_Models_Meta::UpdateByNameAndOccurenceId()
137
  * @param string $name - Meta name.
138
- * @param mixed $value - Meta value.
 
 
139
  */
140
  public function UpdateMetaValue( $name, $value ) {
141
  $model = new WSAL_Models_Meta();
142
- $model->UpdateByNameAndOccurenceId( $name, $value, $this->getId() );
143
  }
144
 
145
  /**
146
- * Returns a key-value pair of meta data.
147
  *
148
- * @see WSAL_Adapters_MySQL_Occurrence::GetMultiMeta()
149
  * @return array
 
150
  */
151
  public function GetMetaArray() {
152
  $result = array();
@@ -154,11 +283,16 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
154
  foreach ( $metas as $meta ) {
155
  $result[ $meta->name ] = maybe_unserialize( $meta->value );
156
  }
 
 
 
 
 
157
  return $result;
158
  }
159
 
160
  /**
161
- * Creates or updates all meta data passed as an array of meta-key/meta-value pairs.
162
  *
163
  * @param array $data - New meta data.
164
  */
@@ -166,6 +300,9 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
166
  foreach ( (array) $data as $key => $val ) {
167
  $this->SetMetaValue( $key, $val );
168
  }
 
 
 
169
  }
170
 
171
  /**
@@ -190,24 +327,6 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
190
  if ( null !== $alert_object && method_exists( $alert_object, 'GetMessage' ) ) {
191
  $this->_cachedMessage = $alert_object->GetMessage( $meta_array, $this->_cachedMessage, $this->getId(), $context );
192
  } else {
193
- /**
194
- * Reaching this point means we have an event we don't know
195
- * about. It could be a custom event or possibly a removed
196
- * event.
197
- *
198
- * We currently have 2 sets of custom events that we can flag
199
- * specific messages about. WPForms and BBPress. Both are
200
- * available as plugin add-ons.
201
- *
202
- * @since 4.0.2
203
- */
204
- $addon_event_codes = array(
205
- 'wfcm' => array(
206
- 'name' => __( 'WFCM', 'wp-security-audit-log' ),
207
- 'event_ids' => array( 6028, 6029, 6030, 6031, 6032, 6033 ),
208
- ),
209
- );
210
-
211
  // Filter to allow items to be added elsewhere.
212
  $addon_event_codes = apply_filters( 'wsal_addon_event_codes', $addon_event_codes );
213
 
@@ -244,7 +363,9 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
244
  * @return boolean True on success, false on failure.
245
  */
246
  public function Delete() {
247
- foreach ( $this->getAdapter()->GetMeta() as $meta ) {
 
 
248
  $meta->Delete();
249
  }
250
  return parent::Delete();
@@ -264,14 +385,10 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
264
  /**
265
  * Gets the Client IP.
266
  *
267
- * @param array $meta - Occurrence meta array.
268
  * @return string IP address of request.
269
  */
270
- public function GetSourceIP( $meta = null ) {
271
- if ( null === $meta ) {
272
- return $this->GetMetaValue( 'ClientIP', '' );
273
- }
274
- return isset( $meta['ClientIP'] ) ? $meta['ClientIP'] : '';
275
  }
276
 
277
  /**
@@ -293,14 +410,14 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
293
  /**
294
  * Gets user roles.
295
  *
296
- * @param array $meta - Occurrence meta array.
297
  * @return array Array of user roles.
298
  */
299
- public function GetUserRoles( $meta = null ) {
300
- if ( null === $meta ) {
301
- return $this->GetMetaValue( 'CurrentUserRoles', array() );
302
- }
303
- return isset( $meta['CurrentUserRoles'] ) ? $meta['CurrentUserRoles'] : array();
 
304
  }
305
 
306
  /**
@@ -338,8 +455,8 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
338
  * @param array $args - Query args.
339
  * @return WSAL_Models_Occurrence[]
340
  */
341
- public function CheckUnKnownUsers( $args = array() ) {
342
- return $this->getAdapter()->CheckUnKnownUsers( $args );
343
  }
344
 
345
  /**
@@ -362,4 +479,34 @@ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
362
  public function GetByPostID( $post_id ) {
363
  return $this->getAdapter()->GetByPostID( $post_id );
364
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  }
21
  */
22
  class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
23
 
24
+ public static $migrated_meta = array(
25
+ 'ClientIP' => 'client_ip',
26
+ 'Severity' => 'severity',
27
+ 'Object' => 'object',
28
+ 'EventType' => 'event_type',
29
+ 'UserAgent' => 'user_agent',
30
+ 'CurrentUserRoles' => 'user_roles',
31
+ 'Username' => 'username',
32
+ 'CurrentUserID' => 'user_id',
33
+ 'SessionID' => 'session_id',
34
+ 'PostStatus' => 'post_status',
35
+ 'PostType' => 'post_type',
36
+ 'PostID' => 'post_id'
37
+ );
38
+
39
  /**
40
  * Occurrence ID.
41
  *
65
  public $created_on = 0.0;
66
 
67
  /**
68
+ * Client IP address.
69
+ *
70
+ * @var string
71
+ * @since 4.4.0
72
+ */
73
+ public $client_ip = '';
74
+
75
+ /**
76
+ * Severity.
77
  *
78
+ * @var int
79
+ * @since 4.4.0
80
  */
81
+ public $severity = '';
82
 
83
  /**
84
+ * Event object.
85
  *
86
+ * @var string
87
+ * @since 4.4.0
88
+ */
89
+ public $object = '';
90
+
91
+ /**
92
+ * Event type.
93
+ *
94
+ * @var string
95
+ * @since 4.4.0
96
  */
97
+ public $event_type = '';
98
+
99
+ /**
100
+ * User agent string.
101
+ *
102
+ * @var string
103
+ * @since 4.4.0
104
+ */
105
+ public $user_agent = '';
106
+
107
+ /**
108
+ * Comma separated user roles of the user belonging to the event.
109
+ *
110
+ * @var string
111
+ * @since 4.4.0
112
+ */
113
+ public $user_roles = '';
114
+
115
+ /**
116
+ * Username of the user belonging to the event.
117
+ *
118
+ * @var string
119
+ * @since 4.4.0
120
+ */
121
+ public $username = null;
122
+
123
+ /**
124
+ * User ID of the user belonging to the event.
125
+ *
126
+ * @var int
127
+ * @since 4.4.0
128
+ */
129
+ public $user_id = null;
130
+
131
+ /**
132
+ * Session ID.
133
+ *
134
+ * @var string
135
+ * @since 4.4.0
136
+ */
137
+ public $session_id = '';
138
+
139
+ /**
140
+ * Post status.
141
+ *
142
+ * @var string
143
+ * @since 4.4.0
144
+ */
145
+ public $post_status = '';
146
+
147
+ /**
148
+ * Post status.
149
+ *
150
+ * @var string
151
+ * @since 4.4.0
152
+ */
153
+ public $post_type = '';
154
+
155
+ /**
156
+ * Post ID.
157
+ *
158
+ * @var int
159
+ * @since 4.4.0
160
+ */
161
+ public $post_id = 0;
162
 
163
  /**
164
  * Model Name.
198
  * @see WSAL_Adapters_MySQL_Occurrence::GetNamedMeta()
199
  */
200
  public function GetMetaValue( $name, $default = array() ) {
201
+ $result = $default;
202
+
203
+ // check if the meta is part of the occurrences table
204
+ if ( in_array( $name, array_keys( self::$migrated_meta ) ) ) {
205
+ $property_name = self::$migrated_meta[ $name ];
206
+ if ( property_exists( $this, $property_name ) ) {
207
+ $result = $this->$property_name;
208
+ }
209
+ } else {
210
+ // Get meta adapter.
211
+ $meta = $this->getAdapter()->GetNamedMeta( $this, $name );
212
+ if ( is_null( $meta ) || ! array_key_exists( 'value', $meta ) ) {
213
+ return $default;
214
+ }
215
+
216
+ $result = $meta['value'];
217
+ }
218
+
219
+ $result = maybe_unserialize( $result );
220
+ if ( 'CurrentUserRoles' === $name && is_string( $result ) ) {
221
+ $result = preg_replace( '/[\[\]"]/', '', $result );
222
+ $result = explode( ',', $result );
223
  }
224
 
225
+ return $result;
226
  }
227
 
228
  /**
229
  * Sets the value of a meta item (creates or updates meta item).
230
  *
231
  * @param string $name - Meta name.
232
+ * @param mixed $value - Meta value.
233
  */
234
  public function SetMetaValue( $name, $value ) {
235
  // check explicitly for `0` string values.
236
  if ( '0' === $value || ! empty( $value ) ) {
237
+ // check if the meta is part of the occurrences table
238
+ if ( in_array( $name, array_keys( self::$migrated_meta ) ) ) {
239
+ $property_name = self::$migrated_meta[ $name ];
240
+ if ( property_exists( $this, $property_name ) ) {
241
+ if ( 'CurrentUserRoles' === $name ) {
242
+ $value = maybe_unserialize( $value );
243
+ if ( is_array( $value ) && ! empty( $value ) ) {
244
+ $this->$property_name = implode( ',', $value );
245
+ }
246
+ } else {
247
+ $this->$property_name = $value;
248
+ }
249
+ }
250
+ } else {
251
+ // Get meta adapter.
252
+ $model = new WSAL_Models_Meta();
253
+ $model->occurrence_id = $this->getId();
254
+ $model->name = $name;
255
+ $model->value = maybe_serialize( $value );
256
+ $model->SaveMeta();
257
+ }
258
  }
259
  }
260
 
261
  /**
262
  * Update Metadata of this occurrence by name.
263
  *
 
264
  * @param string $name - Meta name.
265
+ * @param mixed $value - Meta value.
266
+ *
267
+ *@see WSAL_Models_Meta::UpdateByNameAndOccurrenceId()
268
  */
269
  public function UpdateMetaValue( $name, $value ) {
270
  $model = new WSAL_Models_Meta();
271
+ $model->UpdateByNameAndOccurrenceId( $name, $value, $this->getId() );
272
  }
273
 
274
  /**
275
+ * Returns a key-value pair of metadata.
276
  *
 
277
  * @return array
278
+ * @see WSAL_Adapters_MySQL_Occurrence::GetMultiMeta()
279
  */
280
  public function GetMetaArray() {
281
  $result = array();
283
  foreach ( $metas as $meta ) {
284
  $result[ $meta->name ] = maybe_unserialize( $meta->value );
285
  }
286
+
287
+ foreach ( self::$migrated_meta as $meta_key => $column_name ) {
288
+ $result[ $meta_key ] = $this->$column_name;
289
+ }
290
+
291
  return $result;
292
  }
293
 
294
  /**
295
+ * Creates or updates all metadata passed as an array of meta-key/meta-value pairs.
296
  *
297
  * @param array $data - New meta data.
298
  */
300
  foreach ( (array) $data as $key => $val ) {
301
  $this->SetMetaValue( $key, $val );
302
  }
303
+
304
+ // the occurrence object itself needs to be saved again as some metadata is stored as its properties
305
+ $this->Save();
306
  }
307
 
308
  /**
327
  if ( null !== $alert_object && method_exists( $alert_object, 'GetMessage' ) ) {
328
  $this->_cachedMessage = $alert_object->GetMessage( $meta_array, $this->_cachedMessage, $this->getId(), $context );
329
  } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  // Filter to allow items to be added elsewhere.
331
  $addon_event_codes = apply_filters( 'wsal_addon_event_codes', $addon_event_codes );
332
 
363
  * @return boolean True on success, false on failure.
364
  */
365
  public function Delete() {
366
+ /** @var WSAL_Adapters_MySQL_Occurrence $adapter */
367
+ $adapter= $this->getAdapter();
368
+ foreach ( $adapter->GetMultiMeta() as $meta ) {
369
  $meta->Delete();
370
  }
371
  return parent::Delete();
385
  /**
386
  * Gets the Client IP.
387
  *
 
388
  * @return string IP address of request.
389
  */
390
+ public function GetSourceIP() {
391
+ return $this->GetMetaValue( 'ClientIP', array() );
 
 
 
392
  }
393
 
394
  /**
410
  /**
411
  * Gets user roles.
412
  *
 
413
  * @return array Array of user roles.
414
  */
415
+ public function GetUserRoles() {
416
+ return $this->GetMetaValue( 'CurrentUserRoles', array() );
417
+ }
418
+
419
+ public function SetUserRoles( $roles ) {
420
+ $this->user_roles = is_array($roles) ? implode( ',', $roles ) : $roles;
421
  }
422
 
423
  /**
455
  * @param array $args - Query args.
456
  * @return WSAL_Models_Occurrence[]
457
  */
458
+ public function CheckUnknownUsers( $args = array() ) {
459
+ return $this->getAdapter()->CheckUnknownUsers( $args );
460
  }
461
 
462
  /**
479
  public function GetByPostID( $post_id ) {
480
  return $this->getAdapter()->GetByPostID( $post_id );
481
  }
482
+
483
+ /**
484
+ * @inheritDoc
485
+ *
486
+ * Extends the default mechanism for loading data to handle the migrated meta fields in version 4.4.0.
487
+ *
488
+ * @since 4.4.0
489
+ */
490
+ public function LoadData( $data ) {
491
+ $copy = get_class( $this );
492
+ $copy = new $copy();
493
+ foreach ( (array) $data as $key => $val ) {
494
+ if ( ! is_null( $val ) && in_array($key, ['user_id', 'username'])) {
495
+ // username and user_id cannot have the default value set because some database queries rely on having
496
+ // null values in the database
497
+ if ( 'user_id' === $key ) {
498
+ $this->user_id = intval( $val );
499
+ } else if ( 'username' === $key ) {
500
+ $this->username = (string) $val;
501
+ }
502
+ } else if ( 'roles' === $key ) {
503
+ $this->SetUserRoles($val);
504
+ } else if ( isset( $copy->$key ) ) {
505
+ // default type casting is applied to the rest of the fields
506
+ $this->$key = $this->cast_to_correct_type( $copy, $key, $val );
507
+ }
508
+ }
509
+
510
+ return $this;
511
+ }
512
  }
classes/Models/Query.php CHANGED
@@ -82,7 +82,7 @@ class WSAL_Models_Query {
82
  *
83
  * @var bool
84
  */
85
- protected $useDefaultAdapter = false;
86
 
87
  /**
88
  * Method: Constructor.
@@ -101,7 +101,8 @@ class WSAL_Models_Query {
101
  if ( ! empty( $this->connector ) ) {
102
  return $this->connector;
103
  }
104
- if ( $this->useDefaultAdapter ) {
 
105
  $this->connector = WSAL_Connector_ConnectorFactory::GetDefaultConnector();
106
  } else {
107
  $this->connector = WSAL_Connector_ConnectorFactory::GetConnector();
@@ -112,7 +113,7 @@ class WSAL_Models_Query {
112
  /**
113
  * Gets the adapter.
114
  *
115
- * @return WSAL_Adapters_MySQL_Query
116
  */
117
  public function getAdapter() {
118
  return $this->getConnector()->getAdapter( 'Query' );
82
  *
83
  * @var bool
84
  */
85
+ protected $use_default_adapter = false;
86
 
87
  /**
88
  * Method: Constructor.
101
  if ( ! empty( $this->connector ) ) {
102
  return $this->connector;
103
  }
104
+
105
+ if ( $this->use_default_adapter ) {
106
  $this->connector = WSAL_Connector_ConnectorFactory::GetDefaultConnector();
107
  } else {
108
  $this->connector = WSAL_Connector_ConnectorFactory::GetConnector();
113
  /**
114
  * Gets the adapter.
115
  *
116
+ * @return WSAL_Adapters_QueryInterface
117
  */
118
  public function getAdapter() {
119
  return $this->getConnector()->getAdapter( 'Query' );
classes/Multisite/NetworkWide/AbstractTracker.php CHANGED
@@ -32,7 +32,7 @@ abstract class AbstractTracker implements TrackerInterface {
32
  /**
33
  * The key used to store data under.
34
  *
35
- * You should always redine this in extended classes.
36
  *
37
  * @since 3.5.2
38
  * @var string
32
  /**
33
  * The key used to store data under.
34
  *
35
+ * You should always redefine this in extended classes.
36
  *
37
  * @since 3.5.2
38
  * @var string
classes/Multisite/NetworkWide/TrackerInterface.php CHANGED
@@ -64,8 +64,8 @@ interface TrackerInterface {
64
  public static function get_network_data_list( $site_id = 0 );
65
 
66
  /**
67
- * Should setup the $data property to hold it's data. In format of a
68
- * single dimentional array.
69
  *
70
  * @method generate_data
71
  * @since 3.5.2
64
  public static function get_network_data_list( $site_id = 0 );
65
 
66
  /**
67
+ * Should setup the $data property to hold its data. In format of a
68
+ * single dimensional array.
69
  *
70
  * @method generate_data
71
  * @since 3.5.2
classes/ReportArgs.php CHANGED
@@ -125,6 +125,27 @@ class WSAL_ReportArgs {
125
  */
126
  public $code__in;
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  /**
129
  * An array of post types to include in report.
130
  *
@@ -132,6 +153,13 @@ class WSAL_ReportArgs {
132
  */
133
  public $post_type__in;
134
 
 
 
 
 
 
 
 
135
  /**
136
  * An array of post statuses to include in report.
137
  *
@@ -139,149 +167,229 @@ class WSAL_ReportArgs {
139
  */
140
  public $post_status__in;
141
 
 
 
 
 
 
 
 
142
  /**
143
  * @param array $filters
144
- * @param WSAL_AlertManager $alert_manager
145
  *
146
- * @return false
147
  */
148
- public static function build_from_alternative_filters( $filters, $alert_manager ) {
 
 
 
 
 
 
 
 
 
 
149
 
150
- $result = new WSAL_ReportArgs();
 
151
 
152
- $sites = null;
153
- if ( is_array( $sites ) && ! empty( $sites ) ) {
154
- $result->site__in = $sites;
155
- }
 
 
 
156
 
157
- if ( ! empty( $filters['users'] ) ) {
158
- $result->user__in = $alert_manager->get_user_ids( $filters['users'] );
159
- }
160
 
161
- if ( ! empty( $filters['users-exclude'] ) ) {
162
- $result->user__not_in = $alert_manager->get_user_ids( $filters['users-exclude'] );
163
  }
164
 
165
- if ( is_array( $filters['roles'] ) && ! empty( $filters['roles'] ) ) {
166
- $result->role__in = $filters['roles'];
167
  }
168
 
169
- if ( is_array( $filters['roles-exclude'] ) && ! empty( $filters['roles-exclude'] ) ) {
170
- $result->role__not_in = $filters['roles-exclude'];
171
  }
172
-
173
- $ip_addresses = empty( $filters['ip-addresses'] ) ? null : $filters['ip-addresses'];
174
- $alert_groups = $filters['alert-codes']['groups'];
175
- $alert_codes = $filters['alert-codes']['alerts'];
176
- $date_start = empty( $filters['date-range']['start'] ) ? null : $filters['date-range']['start'];
177
- $date_end = empty( $filters['date-range']['end'] ) ? null : $filters['date-range']['end'];
178
-
179
- // extract full list of alert codes
180
- $codes = $alert_manager->get_codes_by_groups( $alert_groups, $alert_codes );
181
-
182
 
183
-
184
-
185
- if ( is_array( $ip_addresses ) && ! empty( $ip_addresses ) ) {
186
- $result->ip__in = $ip_addresses;
187
  }
188
 
189
- if ( is_array( $codes ) && ! empty( $codes ) ) {
190
- $result->code__in = $codes;
191
  }
192
 
193
- if ( $date_start ) {
194
- $result->start_date = $date_start;
195
  }
196
 
197
- if ( $date_end ) {
198
- $result->end_date = $date_end;
199
  }
200
 
201
- return $result;
202
- }
203
-
204
- /**
205
- * @param array $filters An array of filters as defined in the Reports extension form.
206
- * @param WSAL_Rep_Common $report_utils
207
- *
208
- * @return WSAL_ReportArgs
209
- */
210
- public static function build_from_extension_filters( $filters, $report_utils ) {
211
-
212
- $result = new WSAL_ReportArgs();
213
 
214
- if ( is_array( $filters['sites'] ) && ! empty( $filters['sites'] ) ) {
215
- $result->site__in = $filters['sites'];
216
  }
217
 
218
- if ( is_array( $filters['sites-exclude'] ) && ! empty( $filters['sites-exclude'] ) ) {
219
- $result->site__not_in = $filters['sites-exclude'];
220
  }
221
 
222
- if ( is_array( $filters['users'] ) && ! empty( $filters['users'] ) ) {
223
- $result->user__in = $filters['users'];
224
  }
225
 
226
- if ( is_array( $filters['users-exclude'] ) && ! empty( $filters['users-exclude'] ) ) {
227
- $result->user__not_in = $filters['users-exclude'];
228
  }
229
 
230
- if ( is_array( $filters['roles'] ) && ! empty( $filters['roles'] ) ) {
231
- $result->role__in = $filters['roles'];
232
  }
233
 
234
- if ( is_array( $filters['roles-exclude'] ) && ! empty( $filters['roles-exclude'] ) ) {
235
- $result->role__not_in = $filters['roles-exclude'];
236
  }
237
 
238
- if ( is_array( $filters['alert_codes_post_types'] ) && ! empty( $filters['alert_codes_post_types'] ) ) {
239
- $result->post_type__in = $filters['alert_codes_post_types'];
240
  }
241
 
242
- if ( is_array( $filters['alert_codes_post_statuses'] ) && ! empty( $filters['alert_codes_post_statuses'] ) ) {
243
- $result->post_status__in = $filters['alert_codes_post_statuses'];
244
  }
245
 
246
- if ( is_array( $filters['objects'] ) && ! empty( $filters['objects'] ) ) {
247
  $result->object__in = $filters['objects'];
248
  }
249
 
250
- if ( is_array( $filters['objects-exclude'] ) && ! empty( $filters['objects-exclude'] ) ) {
251
  $result->object__not_in = $filters['objects-exclude'];
252
  }
253
 
254
- if ( is_array( $filters['event-types'] ) && ! empty( $filters['event-types'] ) ) {
255
  $result->type__in = $filters['event-types'];
256
  }
257
 
258
- if ( is_array( $filters['event-types-exclude'] ) && ! empty( $filters['event-types-exclude'] ) ) {
259
  $result->type__not_in = $filters['event-types-exclude'];
260
  }
261
 
262
- if ( is_array( $filters['ip-addresses'] ) && ! empty( $filters['ip-addresses'] ) ) {
263
  $result->ip__in = $filters['ip-addresses'];
264
  }
265
 
266
- if ( is_array( $filters['ip-addresses-exclude'] ) && ! empty( $filters['ip-addresses-exclude'] ) ) {
267
  $result->ip__not_in = $filters['ip-addresses-exclude'];
268
  }
269
 
270
- // extract full list of alert codes
271
- $_codes = $report_utils->GetCodesByGroups( $filters['alert_codes_groups'], $filters['alert_codes_alerts'] );
272
-
273
  if ( is_array( $_codes ) && ! empty( $_codes ) ) {
274
  $result->code__in = $_codes;
275
  }
276
 
277
- if ( $filters['date_range_start'] ) {
 
 
 
 
 
278
  $result->start_date = $filters['date_range_start'];
279
  }
280
 
281
- if ( $filters['date_range_end'] ) {
282
  $result->end_date = $filters['date_range_end'];
283
  }
284
 
285
  return $result;
286
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  }
125
  */
126
  public $code__in;
127
 
128
+ /**
129
+ * An array of event IDs to exclude from report.
130
+ *
131
+ * @var int[]
132
+ */
133
+ public $code__not_in;
134
+
135
+ /**
136
+ * An array of post IDs to include in report.
137
+ *
138
+ * @var string[]
139
+ */
140
+ public $post__in;
141
+
142
+ /**
143
+ * An array of post IDs to exclude from report.
144
+ *
145
+ * @var string[]
146
+ */
147
+ public $post__not_in;
148
+
149
  /**
150
  * An array of post types to include in report.
151
  *
153
  */
154
  public $post_type__in;
155
 
156
+ /**
157
+ * An array of post types to exclude from report.
158
+ *
159
+ * @var string[]
160
+ */
161
+ public $post_type__not_in;
162
+
163
  /**
164
  * An array of post statuses to include in report.
165
  *
167
  */
168
  public $post_status__in;
169
 
170
+ /**
171
+ * An array of post statuses to exclude from report.
172
+ *
173
+ * @var string[]
174
+ */
175
+ public $post_status__not_in;
176
+
177
  /**
178
  * @param array $filters
 
179
  *
180
+ * @return WSAL_ReportArgs
181
  */
182
+ public static function build_from_alternative_filters( $filters ) {
183
+ $_filters = array();
184
+ foreach ( $filters as $key => $value ) {
185
+ if ( is_array( $value ) && ! is_int( array_key_first( $value ) ) ) {
186
+ foreach ( $value as $sub_key => $sub_value ) {
187
+ $_filters[ $key . '_' . $sub_key ] = $sub_value;
188
+ }
189
+ } else {
190
+ $_filters[ $key ] = $value;
191
+ }
192
+ }
193
 
194
+ return self::build_from_extension_filters( $_filters, WpSecurityAuditLog::GetInstance()->reports_util );
195
+ }
196
 
197
+ /**
198
+ * @param array $filters An array of filters as defined in the Reports extension form.
199
+ * @param WSAL_Rep_Common $report_utils Reporting utils.
200
+ *
201
+ * @return WSAL_ReportArgs
202
+ */
203
+ public static function build_from_extension_filters( $filters, $report_utils ) {
204
 
205
+ $result = new WSAL_ReportArgs();
 
 
206
 
207
+ if ( self::is_field_present_and_non_empty_array( 'sites', $filters ) ) {
208
+ $result->site__in = $filters['sites'];
209
  }
210
 
211
+ if ( self::is_field_present_and_non_empty_array( 'sites-exclude', $filters ) ) {
212
+ $result->site__not_in = $filters['sites-exclude'];
213
  }
214
 
215
+ if ( self::is_field_present_and_non_empty_array( 'users', $filters ) ) {
216
+ $result->user__in = self::extract_user_ids( $filters['users'] );
217
  }
 
 
 
 
 
 
 
 
 
 
218
 
219
+ if ( self::is_field_present_and_non_empty_array( 'users-exclude', $filters ) ) {
220
+ $result->user__not_in = self::extract_user_ids( $filters['users-exclude'] );
 
 
221
  }
222
 
223
+ if ( self::is_field_present_and_non_empty_array( 'roles', $filters ) ) {
224
+ $result->role__in = $filters['roles'];
225
  }
226
 
227
+ if ( self::is_field_present_and_non_empty_array( 'roles-exclude', $filters ) ) {
228
+ $result->role__not_in = $filters['roles-exclude'];
229
  }
230
 
231
+ if ( self::is_field_present_and_non_empty_array( 'post_ids', $filters ) ) {
232
+ $result->post__in = $filters['post_ids'];
233
  }
234
 
235
+ if ( self::is_field_present_and_non_empty_array( 'post_ids-exclude', $filters ) ) {
236
+ $result->post__not_in = $filters['post_ids-exclude'];
237
+ }
 
 
 
 
 
 
 
 
 
238
 
239
+ if ( self::is_field_present_and_non_empty_array( 'alert_codes_post_types', $filters ) ) {
240
+ $result->post_type__in = $filters['alert_codes_post_types'];
241
  }
242
 
243
+ if ( self::is_field_present_and_non_empty_array( 'post_types', $filters ) ) {
244
+ $result->post_type__in = $filters['post_types'];
245
  }
246
 
247
+ if ( self::is_field_present_and_non_empty_array( 'alert_codes_post_types-exclude', $filters ) ) {
248
+ $result->post_type__not_in = $filters['alert_codes_post_types-exclude'];
249
  }
250
 
251
+ if ( self::is_field_present_and_non_empty_array( 'post_types-exclude', $filters ) ) {
252
+ $result->post_type__not_in = $filters['post_types-exclude'];
253
  }
254
 
255
+ if ( self::is_field_present_and_non_empty_array( 'alert_codes_post_statuses', $filters ) ) {
256
+ $result->post_status__in = $filters['alert_codes_post_statuses'];
257
  }
258
 
259
+ if ( self::is_field_present_and_non_empty_array( 'post_statuses', $filters ) ) {
260
+ $result->post_status__in = $filters['post_statuses'];
261
  }
262
 
263
+ if ( self::is_field_present_and_non_empty_array( 'alert_codes_post_statuses-exclude', $filters ) ) {
264
+ $result->post_status__not_in = $filters['alert_codes_post_statuses-exclude'];
265
  }
266
 
267
+ if ( self::is_field_present_and_non_empty_array( 'post_statuses-exclude', $filters ) ) {
268
+ $result->post_status__not_in = $filters['post_statuses-exclude'];
269
  }
270
 
271
+ if ( self::is_field_present_and_non_empty_array( 'objects', $filters ) ) {
272
  $result->object__in = $filters['objects'];
273
  }
274
 
275
+ if ( self::is_field_present_and_non_empty_array( 'objects-exclude', $filters ) ) {
276
  $result->object__not_in = $filters['objects-exclude'];
277
  }
278
 
279
+ if ( self::is_field_present_and_non_empty_array( 'event-types', $filters ) ) {
280
  $result->type__in = $filters['event-types'];
281
  }
282
 
283
+ if ( self::is_field_present_and_non_empty_array( 'event-types-exclude', $filters ) ) {
284
  $result->type__not_in = $filters['event-types-exclude'];
285
  }
286
 
287
+ if ( self::is_field_present_and_non_empty_array( 'ip-addresses', $filters ) ) {
288
  $result->ip__in = $filters['ip-addresses'];
289
  }
290
 
291
+ if ( self::is_field_present_and_non_empty_array( 'ip-addresses-exclude', $filters ) ) {
292
  $result->ip__not_in = $filters['ip-addresses-exclude'];
293
  }
294
 
295
+ $_codes = self::get_codes( $filters, 'alert_codes_groups', 'alert_codes_alerts', $report_utils );
 
 
296
  if ( is_array( $_codes ) && ! empty( $_codes ) ) {
297
  $result->code__in = $_codes;
298
  }
299
 
300
+ $_excluded_codes = self::get_codes( $filters, 'alert_codes_groups-exclude', 'alert_codes_alerts-exclude', $report_utils );
301
+ if ( is_array( $_excluded_codes ) && ! empty( $_excluded_codes ) ) {
302
+ $result->code__not_in = $_excluded_codes;
303
+ }
304
+
305
+ if ( array_key_exists( 'date_range_start', $filters ) && $filters['date_range_start'] ) {
306
  $result->start_date = $filters['date_range_start'];
307
  }
308
 
309
+ if ( array_key_exists( 'date_range_end', $filters ) && $filters['date_range_end'] ) {
310
  $result->end_date = $filters['date_range_end'];
311
  }
312
 
313
  return $result;
314
  }
315
+
316
+ /**
317
+ * Checks if the key is present in the array and the value is non-empty array.
318
+ *
319
+ * @param string $key Key to look for.
320
+ * @param array $array Report filtering data.
321
+ *
322
+ * @return bool True if the key is present in the array and the value is non-empty array.
323
+ *
324
+ * @since 4.4.0
325
+ */
326
+ private static function is_field_present_and_non_empty_array( $key, $array ) {
327
+ return array_key_exists( $key, $array ) && is_array( $array[ $key ] ) && ! empty( $array[ $key ] );
328
+ }
329
+
330
+ /**
331
+ * Determines all alert IDs/codes for given array of data. The groups and keys are located using the provided keys.
332
+ *
333
+ * @param array $array Filter data.
334
+ * @param string $groups_key Key of the event groups array.
335
+ * @param string $codes_key Key of the event codes array.
336
+ * @param WSAL_Rep_Common $report_utils Report utilities.
337
+ *
338
+ * @return int[]
339
+ *
340
+ * @since 4.4.0
341
+ */
342
+ private static function get_codes( $array, $groups_key, $codes_key, $report_utils ) {
343
+ $groups = self::is_field_present_and_non_empty_array( $groups_key, $array ) ? $array[ $groups_key ] : array();
344
+ $alerts = self::is_field_present_and_non_empty_array( $codes_key, $array ) ? $array[ $codes_key ] : array();
345
+
346
+ $result = $report_utils->GetCodesByGroups( $groups, $alerts, false );
347
+ if ( false === $result ) {
348
+ return array();
349
+ }
350
+
351
+ return $result;
352
+ }
353
+
354
+ /**
355
+ * Determines user ID based on given value.
356
+ *
357
+ * @param int|string $value User ID or login username.
358
+ *
359
+ * @return int User ID.
360
+ * @since 4.4.0
361
+ */
362
+ public static function grab_uid( $value ) {
363
+ if ( is_numeric( $value ) ) {
364
+ return intval( $value );
365
+ }
366
+
367
+ // Load user by id.
368
+ $user = get_user_by( 'login', $value );
369
+ if ( $user instanceof WP_User ) {
370
+ return $user->ID;
371
+ }
372
+
373
+ return 0;
374
+ }
375
+
376
+ /**
377
+ * Extracts user IDs.
378
+ *
379
+ * @param array $values User IDs or login usernames (or both).
380
+ *
381
+ * @return int[] User IDs.
382
+ * @since 4.4.0
383
+ */
384
+ public static function extract_user_ids( $values ) {
385
+ return array_filter(
386
+ array_map(
387
+ array( __CLASS__, 'grab_uid' ),
388
+ $values
389
+ ),
390
+ function ( $item ) {
391
+ return $item > 0;
392
+ }
393
+ );
394
+ }
395
  }
classes/SensorManager.php CHANGED
@@ -30,16 +30,14 @@ final class WSAL_SensorManager extends WSAL_AbstractSensor {
30
  protected $sensors = array();
31
 
32
  /**
33
- * Method: Constructor.
34
- *
35
- * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
36
  */
37
  public function __construct( WpSecurityAuditLog $plugin ) {
38
  parent::__construct( $plugin );
39
 
40
  // Check sensors before loading for optimization.
41
  add_filter( 'wsal_before_sensor_load', array( $this, 'check_sensor_before_load' ), 10, 2 );
42
- foreach ( glob( dirname( __FILE__ ) . '/Sensors/*.php' ) as $file ) {
43
  $this->AddFromFile( $file );
44
  }
45
 
@@ -55,7 +53,7 @@ final class WSAL_SensorManager extends WSAL_AbstractSensor {
55
  foreach ( $paths as $inc_path ) {
56
  // Check directory.
57
  if ( is_dir( $inc_path ) && is_readable( $inc_path ) ) {
58
- foreach ( glob( $inc_path . '*.php' ) as $file ) {
59
  // Include custom sensor file.
60
  require_once $file;
61
  $file = substr( $file, 0, -4 );
@@ -81,7 +79,7 @@ final class WSAL_SensorManager extends WSAL_AbstractSensor {
81
  }
82
 
83
  /**
84
- * Method: Hook events of the sensors.
85
  */
86
  public function HookEvents() {
87
  foreach ( $this->sensors as $sensor ) {
@@ -105,7 +103,7 @@ final class WSAL_SensorManager extends WSAL_AbstractSensor {
105
  /**
106
  * Filter: `wsal_before_sensor_load`
107
  *
108
- * Check to see if sensor is to be initiaited or not.
109
  *
110
  * @param bool $load_sensor – Set to true if sensor is to be loaded.
111
  * @param string $file – File path.
@@ -189,15 +187,20 @@ final class WSAL_SensorManager extends WSAL_AbstractSensor {
189
 
190
  $frontend_events = WSAL_Settings::get_frontend_events();
191
 
192
- // Check to see if LogInOut, and FrontendRegister sensors should load on login page.
193
  if ( WpSecurityAuditLog::is_login_screen() ) {
194
- if ( 'FrontendRegister' === $filename && ! empty( $frontend_events['register'] )
195
- || 'LogInOut' === $filename ) {
196
  return true;
197
  }
198
  return false; // Any other sensor should not load here.
199
  }
200
 
 
 
 
 
 
 
201
  /**
202
  * WSAL Filter: `wsal_load_public_sensor`
203
  *
@@ -209,7 +212,7 @@ final class WSAL_SensorManager extends WSAL_AbstractSensor {
209
  *
210
  * @param array $public_sensors - List of sensors to be loaded for visitors.
211
  */
212
- $public_sensors = apply_filters( 'wsal_load_public_sensors', array( 'FrontendRegister', 'LogInOut' ) );
213
  if ( WpSecurityAuditLog::is_frontend() && ! is_user_logged_in() && ! in_array( $filename, $public_sensors, true ) ) {
214
  return false;
215
  }
@@ -276,7 +279,7 @@ final class WSAL_SensorManager extends WSAL_AbstractSensor {
276
  }
277
  break;
278
 
279
- case 'FrontendRegister':
280
  if ( is_user_logged_in() || empty( $frontend_events['register'] ) ) {
281
  $load_sensor = false;
282
  }
30
  protected $sensors = array();
31
 
32
  /**
33
+ * {@inheritDoc}
 
 
34
  */
35
  public function __construct( WpSecurityAuditLog $plugin ) {
36
  parent::__construct( $plugin );
37
 
38
  // Check sensors before loading for optimization.
39
  add_filter( 'wsal_before_sensor_load', array( $this, 'check_sensor_before_load' ), 10, 2 );
40
+ foreach ( WSAL_Utilities_FileSystemUtils::read_files_in_folder( dirname( __FILE__ ) . '/Sensors', '*.php' ) as $file ) {
41
  $this->AddFromFile( $file );
42
  }
43
 
53
  foreach ( $paths as $inc_path ) {
54
  // Check directory.
55
  if ( is_dir( $inc_path ) && is_readable( $inc_path ) ) {
56
+ foreach ( WSAL_Utilities_FileSystemUtils::read_files_in_folder( $inc_path, '*.php' ) as $file ) {
57
  // Include custom sensor file.
58
  require_once $file;
59
  $file = substr( $file, 0, -4 );
79
  }
80
 
81
  /**
82
+ * {@inheritDoc}
83
  */
84
  public function HookEvents() {
85
  foreach ( $this->sensors as $sensor ) {
103
  /**
104
  * Filter: `wsal_before_sensor_load`
105
  *
106
+ * Check to see if sensor is to be initiated or not.
107
  *
108
  * @param bool $load_sensor – Set to true if sensor is to be loaded.
109
  * @param string $file – File path.
187
 
188
  $frontend_events = WSAL_Settings::get_frontend_events();
189
 
190
+ // Only LogInOut and Register sensors should load on login page.
191
  if ( WpSecurityAuditLog::is_login_screen() ) {
192
+ if ( in_array( $filename, array( 'Register', 'LogInOut' ) ) ) {
 
193
  return true;
194
  }
195
  return false; // Any other sensor should not load here.
196
  }
197
 
198
+ $default_public_sensors = array( 'Register', 'LogInOut' );
199
+ if ( $this->plugin->IsMultisite() ) {
200
+ // Multisite sign-up is happening on front-end.
201
+ array_push( $default_public_sensors, 'MultisiteSignUp' );
202
+ }
203
+
204
  /**
205
  * WSAL Filter: `wsal_load_public_sensor`
206
  *
212
  *
213
  * @param array $public_sensors - List of sensors to be loaded for visitors.
214
  */
215
+ $public_sensors = apply_filters( 'wsal_load_public_sensors', $default_public_sensors );
216
  if ( WpSecurityAuditLog::is_frontend() && ! is_user_logged_in() && ! in_array( $filename, $public_sensors, true ) ) {
217
  return false;
218
  }
279
  }
280
  break;
281
 
282
+ case 'Register':
283
  if ( is_user_logged_in() || empty( $frontend_events['register'] ) ) {
284
  $load_sensor = false;
285
  }
classes/Sensors/Content.php CHANGED
@@ -100,10 +100,9 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
100
  add_action( 'create_post_tag', array( $this, 'event_tag_creation' ), 10, 1 );
101
  add_action( 'pre_delete_term', array( $this, 'check_taxonomy_term_deletion' ), 10, 2 );
102
  add_filter( 'wp_update_term_data', array( $this, 'event_update_term_data' ), 10, 4 );
103
- add_filter( 'add_post_metadata', array( $this, 'check_changed_meta' ), 10, 4 );
104
- add_filter( 'updated_post_meta', array( $this, 'check_changed_meta' ), 10, 4 );
105
  add_filter( 'delete_post_metadata', array( $this, 'check_deleted_meta' ), 10, 5 );
106
-
107
 
108
  // Check if MainWP Child Plugin exists.
109
  if ( WpSecurityAuditLog::is_mainwp_active() ) {
@@ -124,14 +123,14 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
124
 
125
  // If post exists.
126
  if ( ! empty( $post ) && $post instanceof WP_Post ) {
127
- $this->_old_post = $post;
128
- $this->_old_link = get_permalink( $post_id );
129
- $this->_old_tmpl = $this->get_post_template( $this->_old_post );
130
- $this->_old_cats = $this->get_post_categories( $this->_old_post );
131
- $this->_old_tags = $this->get_post_tags( $this->_old_post );
132
- $this->_old_stky = in_array( $post_id, get_option( 'sticky_posts' ), true );
133
- $this->old_status = $post->post_status;
134
- $this->old_meta = get_post_meta( $post_id );
135
  }
136
  }
137
 
@@ -291,10 +290,10 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
291
 
292
  $event_data = $this->get_post_event_data( $post ); // Get event data.
293
 
294
- // check if this was initiated by a plugin
295
- $request_params = WSAL_Utilities_RequestUtils::get_filtered_request_data();
296
  if ( empty( $request_params['action'] ) && isset( $request_params['page'] ) ) {
297
- $event = 5025;
298
  $event_data = array(
299
  'PostID' => $post->ID,
300
  'PostType' => $post->post_type,
@@ -456,9 +455,9 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
456
 
457
  // Update post URL based on current actual path.
458
  if ( $this->plugin->IsMultisite() && ! is_subdomain_install() ) {
459
- // for multisite using subfolders, remove the subfolder
460
- $subdir_path = parse_url( home_url(), PHP_URL_PATH );
461
- $escaped = str_replace( '/', '\/', preg_quote( $subdir_path ) );
462
  $current_path = preg_replace( '/' . $escaped . '/', '', $current_path );
463
  }
464
 
@@ -603,10 +602,10 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
603
  $this->plugin->alerts->Trigger(
604
  2125,
605
  array(
606
- 'tag' => $new_name,
607
- 'TagLink' => $term_link,
608
- 'old_desc' => $old_desc,
609
- 'new_desc' => $new_desc,
610
  )
611
  );
612
  }
@@ -667,18 +666,20 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
667
  }
668
 
669
  /**
670
- * Check Page Template Update.
671
  *
672
- * @param int $meta_id ID of updated metadata entry.
673
- * @param int $post_id Post ID.
674
- * @param string $meta_key Meta key.
675
- * @param mixed $meta_value Meta value.
676
- *
677
- * @return int $meta_id ID of updated metadata entry.
 
 
678
  */
679
- public function check_changed_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
680
  if ( ! $post_id ) {
681
- return $meta_id;
682
  }
683
 
684
  switch ( $meta_key ) {
@@ -689,75 +690,93 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
689
  $this->check_featured_image_change( $post_id, $meta_value );
690
  break;
691
  default:
692
- return $meta_id;
693
  }
694
 
695
- return $meta_id;
696
  }
697
 
698
  /**
699
- * Check Page Template Update for delitions.
700
- *
701
- * @param bool|null $delete Whether to allow metadata deletion of the given type.
702
- * @param int $meta_id ID of updated metadata entry.
703
- * @param int $post_id Post ID.
704
- * @param string $meta_key Meta key.
705
- * @param mixed $meta_value Meta value.
706
- *
707
- * @return bool|null $delete Whether to allow metadata deletion of the given type.
708
  */
709
- public function check_deleted_meta( $delete, $meta_id, $post_id, $meta_key, $meta_value ) {
710
- if ( ! $post_id ) {
711
- return $delete;
712
- }
713
 
714
- switch ( $meta_key ) {
715
- case '_wp_page_template':
716
- $this->check_template_change( $post_id, $meta_value );
717
- break;
718
- case '_thumbnail_id':
719
- $this->check_featured_image_change( $post_id, $meta_value );
720
- break;
721
- default:
722
- // no other meta keys supported here.
723
- }
724
- return $delete;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
725
  }
726
 
727
  /**
728
  * Check Page Template Update.
729
  *
730
- * @param int $post_id Post ID.
731
- * @param mixed $meta_value Meta value.
732
  */
733
- public function check_template_change( $post_id, $meta_value ) {
734
- $post = get_post( $post_id );
735
- $old_tmpl = ( $this->_old_tmpl && 'page' !== basename( $this->_old_tmpl, '.php' ) ) ? ucwords( str_replace( array( '-', '_' ), ' ', basename( $this->_old_tmpl, '.php' ) ) ) : __( 'Default template', 'wp-security-audit-log' );
736
- $new_tmpl = ( $meta_value ) ? ucwords( str_replace( array( '-', '_' ), ' ', basename( $meta_value ) ) ) : __( 'Default', 'wp-security-audit-log' );
737
-
738
- if ( $old_tmpl !== $new_tmpl ) {
739
- $editor_link = $this->get_editor_link( $post );
740
- $this->plugin->alerts->Trigger(
741
- 2048,
742
- array(
743
- 'PostID' => $post->ID,
744
- 'PostType' => $post->post_type,
745
- 'PostTitle' => $post->post_title,
746
- 'PostStatus' => $post->post_status,
747
- 'PostDate' => $post->post_date,
748
- 'OldTemplate' => $old_tmpl,
749
- 'NewTemplate' => $new_tmpl,
750
- $editor_link['name'] => $editor_link['value'],
751
- )
752
- );
753
- }
754
- }
755
 
756
  /**
757
  * Check Post Featured Image Update.
758
  *
759
- * @param int $post_id Post ID.
760
- * @param mixed $meta_value Meta value.
761
  */
762
  public function check_featured_image_change( $post_id, $meta_value ) {
763
  $previous_featured_image = ( isset( $this->old_meta['_thumbnail_id'][0] ) ) ? wp_get_attachment_metadata( $this->old_meta['_thumbnail_id'][0] ) : false;
@@ -771,14 +790,14 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
771
 
772
  if ( empty( $previous_featured_image['file'] ) && ! empty( $new_featured_image['file'] ) ) {
773
  $event_type = 'added';
774
- } elseif ( ! empty( $previous_featured_image['file'] ) && empty( $new_featured_image['file'] ) ) {
775
  $event_type = 'removed';
776
  }
777
 
778
  $previous_image = is_array( $previous_featured_image ) && array_key_exists( 'file', $previous_featured_image ) ? $previous_featured_image['file'] : __( 'No previous image', 'wp-security-audit-log' );
779
  $new_image = is_array( $new_featured_image ) && array_key_exists( 'file', $new_featured_image ) ? $new_featured_image['file'] : __( 'No image', 'wp-security-audit-log' );
780
 
781
- $post = get_post( $post_id );
782
  $editor_link = $this->get_editor_link( $post );
783
  $this->plugin->alerts->Trigger(
784
  2130,
@@ -915,14 +934,14 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
915
  $this->plugin->alerts->Trigger( $event, $event_data );
916
  } else {
917
 
918
- // so far we assume that the action is initiated by a user, let's check if it was actually initiated
919
- // by a plugin
920
  $request_params = WSAL_Utilities_RequestUtils::get_filtered_request_data();
921
- if ( array_key_exists( 'plugin', $request_params ) && !empty( $request_params['plugin'] ) ) {
922
- // event initiated by a plugin
923
  $plugin_name = $request_params['plugin'];
924
  $plugin_data = get_plugin_data( trailingslashit( WP_PLUGIN_DIR ) . $plugin_name );
925
- $event_data = array(
926
  'PluginName' => ( $plugin_data && isset( $plugin_data['Name'] ) ) ? $plugin_data['Name'] : false,
927
  'PostID' => $new_post->ID,
928
  'PostType' => $new_post->post_type,
100
  add_action( 'create_post_tag', array( $this, 'event_tag_creation' ), 10, 1 );
101
  add_action( 'pre_delete_term', array( $this, 'check_taxonomy_term_deletion' ), 10, 2 );
102
  add_filter( 'wp_update_term_data', array( $this, 'event_update_term_data' ), 10, 4 );
103
+ add_filter( 'add_post_metadata', array( $this, 'check_added_meta' ), 10, 5 );
 
104
  add_filter( 'delete_post_metadata', array( $this, 'check_deleted_meta' ), 10, 5 );
105
+ add_action( 'updated_post_meta', array( $this, 'check_changed_meta' ), 10, 4 );
106
 
107
  // Check if MainWP Child Plugin exists.
108
  if ( WpSecurityAuditLog::is_mainwp_active() ) {
123
 
124
  // If post exists.
125
  if ( ! empty( $post ) && $post instanceof WP_Post ) {
126
+ $this->_old_post = $post;
127
+ $this->_old_link = get_permalink( $post_id );
128
+ $this->_old_tmpl = $this->get_post_template( $this->_old_post );
129
+ $this->_old_cats = $this->get_post_categories( $this->_old_post );
130
+ $this->_old_tags = $this->get_post_tags( $this->_old_post );
131
+ $this->_old_stky = in_array( $post_id, get_option( 'sticky_posts' ), true );
132
+ $this->old_status = $post->post_status;
133
+ $this->old_meta = get_post_meta( $post_id );
134
  }
135
  }
136
 
290
 
291
  $event_data = $this->get_post_event_data( $post ); // Get event data.
292
 
293
+ // Check if this was initiated by a plugin.
294
+ $request_params = WSAL_Utilities_RequestUtils::get_filtered_request_data();
295
  if ( empty( $request_params['action'] ) && isset( $request_params['page'] ) ) {
296
+ $event = 5025;
297
  $event_data = array(
298
  'PostID' => $post->ID,
299
  'PostType' => $post->post_type,
455
 
456
  // Update post URL based on current actual path.
457
  if ( $this->plugin->IsMultisite() && ! is_subdomain_install() ) {
458
+ // For multisite using subfolders, remove the subfolder.
459
+ $subdir_path = parse_url( home_url(), PHP_URL_PATH );
460
+ $escaped = str_replace( '/', '\/', preg_quote( $subdir_path ) );
461
  $current_path = preg_replace( '/' . $escaped . '/', '', $current_path );
462
  }
463
 
602
  $this->plugin->alerts->Trigger(
603
  2125,
604
  array(
605
+ 'tag' => $new_name,
606
+ 'TagLink' => $term_link,
607
+ 'old_desc' => $old_desc,
608
+ 'new_desc' => $new_desc,
609
  )
610
  );
611
  }
666
  }
667
 
668
  /**
669
+ * Checks if selected metadata items have changed. Function is called from different contexts.
670
  *
671
+ * @param int $post_id Post ID.
672
+ * @param string $meta_key Meta key.
673
+ * @param mixed $meta_value Meta value.
674
+ * @param mixed $default_result Default result.
675
+ *
676
+ * @return mixed
677
+ *
678
+ * @since 4.4.0
679
  */
680
+ private function check_selected_meta_change( $post_id, $meta_key, $meta_value, $default_result ) {
681
  if ( ! $post_id ) {
682
+ return $default_result;
683
  }
684
 
685
  switch ( $meta_key ) {
690
  $this->check_featured_image_change( $post_id, $meta_value );
691
  break;
692
  default:
693
+ return $default_result;
694
  }
695
 
696
+ return $default_result;
697
  }
698
 
699
  /**
700
+ * Check Page Template Update.
701
+ *
702
+ * @param int $meta_id ID of updated metadata entry.
703
+ * @param int $post_id Post ID.
704
+ * @param string $meta_key Meta key.
705
+ * @param mixed $meta_value Meta value.
706
+ *
707
+ * @return int ID of updated metadata entry.
 
708
  */
709
+ public function check_changed_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
710
+ return $this->check_selected_meta_change( $post_id, $meta_key, $meta_value, $meta_id );
711
+ }
 
712
 
713
+ /**
714
+ * Check Page Template Update for deletions.
715
+ *
716
+ * @param null|bool $delete Whether to allow metadata deletion of the given type.
717
+ * @param int $object_id ID of the object metadata is for.
718
+ * @param string $meta_key Metadata key.
719
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
720
+ * @param bool $delete_all Whether to delete the matching metadata entries
721
+ * for all objects, ignoring the specified $object_id.
722
+ * Default false.
723
+ *
724
+ * @return bool|null Whether to allow metadata deletion of the given type.
725
+ */
726
+ public function check_deleted_meta( $delete, $object_id, $meta_key, $meta_value, $delete_all ) {
727
+ return $this->check_selected_meta_change( $object_id, $meta_key, $meta_value, $delete );
728
+ }
729
+
730
+ /**
731
+ * Check Page Template Update for additions.
732
+ *
733
+ * @param null|bool $check Whether to allow adding metadata for the given type.
734
+ * @param int $object_id ID of the object metadata is for.
735
+ * @param string $meta_key Metadata key.
736
+ * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
737
+ * @param bool $unique Whether the specified meta key should be unique for the object.
738
+ *
739
+ * @return bool|null Whether to allow metadata addition of the given type.
740
+ *
741
+ * @since 4.4.0
742
+ */
743
+ public function check_added_meta( $check, $object_id, $meta_key, $meta_value, $unique ) {
744
+ return $this->check_selected_meta_change( $object_id, $meta_key, $meta_value, $check );
745
  }
746
 
747
  /**
748
  * Check Page Template Update.
749
  *
750
+ * @param int $post_id Post ID.
751
+ * @param mixed $meta_value Meta value.
752
  */
753
+ public function check_template_change( $post_id, $meta_value ) {
754
+ $post = get_post( $post_id );
755
+ $old_tmpl = ( $this->_old_tmpl && 'page' !== basename( $this->_old_tmpl, '.php' ) ) ? ucwords( str_replace( array( '-', '_' ), ' ', basename( $this->_old_tmpl, '.php' ) ) ) : __( 'Default template', 'wp-security-audit-log' );
756
+ $new_tmpl = ( $meta_value ) ? ucwords( str_replace( array( '-', '_' ), ' ', basename( $meta_value ) ) ) : __( 'Default', 'wp-security-audit-log' );
757
+ if ( $old_tmpl !== $new_tmpl ) {
758
+ $editor_link = $this->get_editor_link( $post );
759
+ $this->plugin->alerts->Trigger(
760
+ 2048,
761
+ array(
762
+ 'PostID' => $post->ID,
763
+ 'PostType' => $post->post_type,
764
+ 'PostTitle' => $post->post_title,
765
+ 'PostStatus' => $post->post_status,
766
+ 'PostDate' => $post->post_date,
767
+ 'OldTemplate' => $old_tmpl,
768
+ 'NewTemplate' => $new_tmpl,
769
+ $editor_link['name'] => $editor_link['value'],
770
+ )
771
+ );
772
+ }
773
+ }
 
774
 
775
  /**
776
  * Check Post Featured Image Update.
777
  *
778
+ * @param int $post_id Post ID.
779
+ * @param mixed $meta_value Meta value.
780
  */
781
  public function check_featured_image_change( $post_id, $meta_value ) {
782
  $previous_featured_image = ( isset( $this->old_meta['_thumbnail_id'][0] ) ) ? wp_get_attachment_metadata( $this->old_meta['_thumbnail_id'][0] ) : false;
790
 
791
  if ( empty( $previous_featured_image['file'] ) && ! empty( $new_featured_image['file'] ) ) {
792
  $event_type = 'added';
793
+ } elseif ( ! empty( $previous_featured_image['file'] ) && empty( $new_featured_image['file'] ) ) {
794
  $event_type = 'removed';
795
  }
796
 
797
  $previous_image = is_array( $previous_featured_image ) && array_key_exists( 'file', $previous_featured_image ) ? $previous_featured_image['file'] : __( 'No previous image', 'wp-security-audit-log' );
798
  $new_image = is_array( $new_featured_image ) && array_key_exists( 'file', $new_featured_image ) ? $new_featured_image['file'] : __( 'No image', 'wp-security-audit-log' );
799
 
800
+ $post = get_post( $post_id );
801
  $editor_link = $this->get_editor_link( $post );
802
  $this->plugin->alerts->Trigger(
803
  2130,
934
  $this->plugin->alerts->Trigger( $event, $event_data );
935
  } else {
936
 
937
+ // So far we assume that the action is initiated by a user, let's check if it was actually initiated
938
+ // by a plugin.
939
  $request_params = WSAL_Utilities_RequestUtils::get_filtered_request_data();
940
+ if ( array_key_exists( 'plugin', $request_params ) && ! empty( $request_params['plugin'] ) ) {
941
+ // Event initiated by a plugin.
942
  $plugin_name = $request_params['plugin'];
943
  $plugin_data = get_plugin_data( trailingslashit( WP_PLUGIN_DIR ) . $plugin_name );
944
+ $event_data = array(
945
  'PluginName' => ( $plugin_data && isset( $plugin_data['Name'] ) ) ? $plugin_data['Name'] : false,
946
  'PostID' => $new_post->ID,
947
  'PostType' => $new_post->post_type,
classes/Sensors/FrontendRegister.php DELETED
@@ -1,62 +0,0 @@
1
- <?php
2
- /**
3
- * Frontend user registration sensor.
4
- *
5
- * @package wsal
6
- */
7
-
8
- if ( ! defined( 'ABSPATH' ) ) {
9
- exit; // Exit if accessed directly.
10
- }
11
-
12
- /**
13
- * Frontend user registration sensor.
14
- */
15
- class WSAL_Sensors_FrontendRegister extends WSAL_AbstractSensor {
16
-
17
- /**
18
- * Listening to events using WP hooks.
19
- */
20
- public function HookEvents() {
21
- /*
22
- * We hook into action 'user_register' because it is part of the function 'wp_insert_user'. Default WordPress
23
- * registration utilizes action 'register_new_user', but we cannot rely a front-end registration implemented
24
- * by a third party to use it.
25
- */
26
- add_action( 'user_register', array( $this, 'event_user_register' ), 10, 1 );
27
- }
28
-
29
- /**
30
- * Triggered when a user is registered.
31
- *
32
- * This function is identical to function WSAL_Sensors_Public::event_user_register. We will keep the duplication
33
- * until some autoloading improvements are made.
34
- *
35
- * @param int $user_id - User ID of the registered user.
36
- */
37
- public function event_user_register( $user_id ) {
38
- $plugin = WpSecurityAuditLog::GetInstance();
39
- $user = get_userdata( $user_id );
40
- $event = $plugin->IsMultisite() ? 4012 : ( is_user_logged_in() ? 4001 : 4000 );
41
- $current_user = wp_get_current_user();
42
-
43
- $new_user_data = array(
44
- 'Username' => $user->user_login,
45
- 'Email' => $user->user_email,
46
- 'Roles' => is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles,
47
- );
48
-
49
- if ( 4000 != $event ) {
50
- $new_user_data['FirstName'] = ! empty( $user->user_firstname ) ? $user->user_firstname : '';
51
- $new_user_data['LastName'] = ! empty( $user->user_lastname ) ? $user->user_lastname : '';
52
- }
53
-
54
- $event_data = array(
55
- 'NewUserID' => $user_id,
56
- 'UserChanger' => ! empty( $current_user ) ? $current_user->user_login : '',
57
- 'NewUserData' => (object) $new_user_data,
58
- );
59
-
60
- $plugin->alerts->Trigger( $event, $event_data, true );
61
- }
62
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/Sensors/LogInOut.php CHANGED
@@ -393,15 +393,13 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
393
  $new = $occ->GetMetaValue( 'Attempts', 0 ) + 1;
394
 
395
  $login_failure_log_limit = $this->GetLoginFailureLogLimit();
396
- if ( - 1 !== (int) $this->GetLoginFailureLogLimit()
397
- && $new > $this->GetLoginFailureLogLimit() ) {
398
- $new = $this->GetLoginFailureLogLimit() . '+';
399
  }
400
 
401
  $occ->UpdateMetaValue( 'Attempts', $new );
402
- $occ->UpdateMetaValue( 'Username', $username );
403
-
404
- // $occ->SetMetaValue('CurrentUserRoles', $user_roles);
405
  $occ->created_on = null;
406
  $occ->Save();
407
  } else {
@@ -419,14 +417,14 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
419
  *
420
  * @return bool
421
  */
422
- function ($manager) {
423
  // skip if 1004 (session block) is already in place
424
  return !$manager->WillOrHasTriggered(1004);
425
  }
426
  );
427
  }
428
  } else {
429
- $occ_unknown = $obj_occurrence->CheckUnKnownUsers(
430
  array(
431
  $ip,
432
  1003,
@@ -445,9 +443,9 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
445
  $new = $occ_unknown->GetMetaValue( 'Attempts', 0 ) + 1;
446
 
447
  // If login attempts pass allowed number of attempts then stop increasing the attempts.
448
- if ( -1 !== (int) $this->GetVisitorLoginFailureLogLimit()
449
- && $new > $this->GetVisitorLoginFailureLogLimit() ) {
450
- $new = $this->GetVisitorLoginFailureLogLimit() . '+';
451
  }
452
 
453
  // Update the number of login attempts.
@@ -462,8 +460,7 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
462
  $occ_unknown->UpdateMetaValue( 'Users', $users );
463
  } else {
464
  // In this case the value doesn't exist so set the value to array.
465
- $users = array();
466
- $users[] = $username;
467
  }
468
 
469
  $occ_unknown->created_on = null;
393
  $new = $occ->GetMetaValue( 'Attempts', 0 ) + 1;
394
 
395
  $login_failure_log_limit = $this->GetLoginFailureLogLimit();
396
+ if ( - 1 !== $login_failure_log_limit
397
+ && $new > $login_failure_log_limit ) {
398
+ $new = $login_failure_log_limit . '+';
399
  }
400
 
401
  $occ->UpdateMetaValue( 'Attempts', $new );
402
+ $occ->username = $username;
 
 
403
  $occ->created_on = null;
404
  $occ->Save();
405
  } else {
417
  *
418
  * @return bool
419
  */
420
+ function ( $manager ) {
421
  // skip if 1004 (session block) is already in place
422
  return !$manager->WillOrHasTriggered(1004);
423
  }
424
  );
425
  }
426
  } else {
427
+ $occ_unknown = $obj_occurrence->CheckUnknownUsers(
428
  array(
429
  $ip,
430
  1003,
443
  $new = $occ_unknown->GetMetaValue( 'Attempts', 0 ) + 1;
444
 
445
  // If login attempts pass allowed number of attempts then stop increasing the attempts.
446
+ $failure_limit = $this->GetVisitorLoginFailureLogLimit();
447
+ if ( -1 !== $failure_limit && $new > $failure_limit ) {
448
+ $new = $failure_limit . '+';
449
  }
450
 
451
  // Update the number of login attempts.
460
  $occ_unknown->UpdateMetaValue( 'Users', $users );
461
  } else {
462
  // In this case the value doesn't exist so set the value to array.
463
+ $users = array( $username );
 
464
  }
465
 
466
  $occ_unknown->created_on = null;
classes/Sensors/Multisite.php CHANGED
@@ -5,7 +5,9 @@
5
  * Multisite sensor file.
6
  *
7
  * @since 1.0.0
 
8
  * @package wsal
 
9
  */
10
 
11
  // Exit if accessed directly.
@@ -14,11 +16,10 @@ if ( ! defined( 'ABSPATH' ) ) {
14
  }
15
 
16
  /**
17
- * Multisite Sensor.
18
  *
19
  * 4010 Existing user added to a site
20
  * 4011 User removed from site
21
- * 4012 New network user created
22
  * 5008 Activated theme on network
23
  * 5009 Deactivated theme from network
24
  * 7000 New site added on the network
@@ -58,7 +59,7 @@ class WSAL_Sensors_Multisite extends WSAL_AbstractSensor {
58
  add_action( 'add_user_to_blog', array( $this, 'EventUserAddedToBlog' ), 10, 3 );
59
  add_action( 'remove_user_from_blog', array( $this, 'EventUserRemovedFromBlog' ), 10, 2 );
60
 
61
- add_action( 'update_site_option', array( $this, 'on_network_option_change'), 10, 4 );
62
  }
63
 
64
  /**
@@ -207,11 +208,13 @@ class WSAL_Sensors_Multisite extends WSAL_AbstractSensor {
207
  public function EventNewBlog( $new_blog ) {
208
  $blog_id = $new_blog->blog_id;
209
 
210
- // site meta data nor options are not setup at this points so get_blog_option and get_home_url are not
211
- // returning anything for the new blog
212
-
213
- // the following code to work out the correct URL for the new site was taken from ms-site.php (WP 5.7)
214
- // @see https://github.com/WordPress/WordPress/blob/5.7/wp-includes/ms-site.php#L673
 
 
215
  $network = get_network( $new_blog->network_id );
216
  if ( ! $network ) {
217
  $network = get_network();
@@ -232,7 +235,7 @@ class WSAL_Sensors_Multisite extends WSAL_AbstractSensor {
232
  array(
233
  'BlogID' => $blog_id,
234
  'SiteName' => $blog_title,
235
- 'BlogURL' => $blog_url
236
  )
237
  );
238
  }
@@ -371,6 +374,7 @@ class WSAL_Sensors_Multisite extends WSAL_AbstractSensor {
371
  * New network user created.
372
  *
373
  * @param WSAL_AlertManager $mgr - Instance of Alert Manager.
 
374
  */
375
  public function MustNotContainCreateUser( WSAL_AlertManager $mgr ) {
376
  return ! $mgr->WillTrigger( 4012 );
5
  * Multisite sensor file.
6
  *
7
  * @since 1.0.0
8
+ *
9
  * @package wsal
10
+ * @subpackage sensors
11
  */
12
 
13
  // Exit if accessed directly.
16
  }
17
 
18
  /**
19
+ * Multisite sensor.
20
  *
21
  * 4010 Existing user added to a site
22
  * 4011 User removed from site
 
23
  * 5008 Activated theme on network
24
  * 5009 Deactivated theme from network
25
  * 7000 New site added on the network
59
  add_action( 'add_user_to_blog', array( $this, 'EventUserAddedToBlog' ), 10, 3 );
60
  add_action( 'remove_user_from_blog', array( $this, 'EventUserRemovedFromBlog' ), 10, 2 );
61
 
62
+ add_action( 'update_site_option', array( $this, 'on_network_option_change' ), 10, 4 );
63
  }
64
 
65
  /**
208
  public function EventNewBlog( $new_blog ) {
209
  $blog_id = $new_blog->blog_id;
210
 
211
+ /*
212
+ * Site meta data nor options are not setup at this points so get_blog_option and get_home_url are not
213
+ * returning anything for the new blog.
214
+ *
215
+ * The following code to work out the correct URL for the new site was taken from ms-site.php (WP 5.7).
216
+ * @see https://github.com/WordPress/WordPress/blob/5.7/wp-includes/ms-site.php#L673
217
+ */
218
  $network = get_network( $new_blog->network_id );
219
  if ( ! $network ) {
220
  $network = get_network();
235
  array(
236
  'BlogID' => $blog_id,
237
  'SiteName' => $blog_title,
238
+ 'BlogURL' => $blog_url,
239
  )
240
  );
241
  }
374
  * New network user created.
375
  *
376
  * @param WSAL_AlertManager $mgr - Instance of Alert Manager.
377
+ * @return bool
378
  */
379
  public function MustNotContainCreateUser( WSAL_AlertManager $mgr ) {
380
  return ! $mgr->WillTrigger( 4012 );
classes/Sensors/MultisiteSignUp.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Sensor: Multisite sign-up
4
+ *
5
+ * Multisite sign-up sensor file.
6
+ *
7
+ * @since 4.4.0
8
+ *
9
+ * @package wsal
10
+ * @subpackage sensors
11
+ */
12
+
13
+ // Exit if accessed directly.
14
+ if ( ! defined( 'ABSPATH' ) ) {
15
+ exit;
16
+ }
17
+
18
+ /**
19
+ * Multisite sign-up sensor.
20
+ *
21
+ * 4013 User has been activated
22
+ * 4024 User has signed up to the network
23
+ *
24
+ * @package wsal
25
+ * @subpackage sensors
26
+ */
27
+ class WSAL_Sensors_MultisiteSignUp extends WSAL_AbstractSensor {
28
+ /**
29
+ * {@inheritDoc}
30
+ */
31
+ public function HookEvents() {
32
+ add_action( 'after_signup_user', array( $this, 'handle_multisite_user_signup' ), 10, 4 );
33
+ add_action( 'wpmu_activate_user', array( $this, 'handle_multisite_user_activation' ), 10, 3 );
34
+ }
35
+
36
+ /**
37
+ * Function handles new multisite user activation.
38
+ *
39
+ * @param int $user_id User ID.
40
+ * @param string $password User password.
41
+ * @param array $meta Signup meta data.
42
+ */
43
+ public function handle_multisite_user_activation( $user_id, $password, $meta ) {
44
+ $user = get_userdata( $user_id );
45
+ if ( ! $user instanceof WP_User ) {
46
+ // Bail if the user is not valid for some reason.
47
+ return;
48
+ }
49
+
50
+ $new_user_data = array(
51
+ 'Roles' => is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles,
52
+ 'Username' => $user->user_login,
53
+ 'FirstName' => ! empty( $user->user_firstname ) ? $user->user_firstname : '',
54
+ 'LastName' => ! empty( $user->user_lastname ) ? $user->user_lastname : '',
55
+ );
56
+
57
+ $event_data = array(
58
+ 'NewUserID' => $user_id,
59
+ 'NewUserData' => (object) $new_user_data,
60
+ 'EditUserLink' => add_query_arg( 'user_id', $user_id, admin_url( 'user-edit.php' ) ),
61
+ );
62
+
63
+ $this->plugin->alerts->TriggerIf( 4013, $event_data, array( $this, 'must_not_contain_create_user' ) );
64
+ }
65
+
66
+ /**
67
+ * Function handles multisite user sign-ups.
68
+ *
69
+ * Caution: there is no entry in the users table at this point.
70
+ *
71
+ * @param string $user The user's requested login name.
72
+ * @param string $user_email The user's email address.
73
+ * @param string $key The user's activation key.
74
+ * @param array $meta Signup meta data. Default empty array.
75
+ */
76
+ public function handle_multisite_user_signup( $user, $user_email, $key, $meta ) {
77
+ $this->plugin->alerts->TriggerIf(
78
+ 4024,
79
+ array(
80
+ 'username' => $user,
81
+ 'email_address' => $user_email,
82
+ ),
83
+ array( $this, 'must_not_contain_create_user' )
84
+ );
85
+ }
86
+
87
+ /**
88
+ * Callback that checks if event 4012 is already in the pipeline.
89
+ *
90
+ * @param WSAL_AlertManager $manager - Instance of WSAL_AlertManager.
91
+ */
92
+ public function must_not_contain_create_user( WSAL_AlertManager $manager ) {
93
+ return ! $manager->WillTrigger( 4012 );
94
+ }
95
+ }
classes/Sensors/Public.php DELETED
@@ -1,64 +0,0 @@
1
- <?php
2
- /**
3
- * Sensor: Public Activity
4
- *
5
- * Public/Visitor activity sensor class file.
6
- *
7
- * @since 3.3
8
- * @package wsal
9
- */
10
-
11
- // Exit if accessed directly.
12
- if ( ! defined( 'ABSPATH' ) ) {
13
- exit;
14
- }
15
-
16
- /**
17
- * System Activity sensor.
18
- *
19
- * @package wsal
20
- */
21
- class WSAL_Sensors_Public extends WSAL_AbstractSensor {
22
-
23
- /**
24
- * Listening to events using WP hooks.
25
- */
26
- public function HookEvents() {
27
- add_action( 'user_register', array( $this, 'event_user_register' ) );
28
- }
29
-
30
- /**
31
- * Triggered when a user is registered.
32
- *
33
- * This function is identical to function WSAL_Sensors_FrontendRegister::event_user_register. We will keep the
34
- * duplication until some autoloading improvements are made.
35
- *
36
- * @param int $user_id - User ID of the registered user.
37
- */
38
- public function event_user_register( $user_id ) {
39
- $plugin = WpSecurityAuditLog::GetInstance();
40
- $user = get_userdata( $user_id );
41
- $event = $plugin->IsMultisite() ? 4012 : ( is_user_logged_in() ? 4001 : 4000 );
42
- $current_user = wp_get_current_user();
43
-
44
- $new_user_data = array(
45
- 'Username' => $user->user_login,
46
- 'Email' => $user->user_email,
47
- 'Roles' => is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles,
48
- );
49
-
50
- if ( 4000 != $event ) {
51
- $new_user_data['FirstName'] = ! empty( $user->user_firstname ) ? $user->user_firstname : '';
52
- $new_user_data['LastName'] = ! empty( $user->user_lastname ) ? $user->user_lastname : '';
53
- }
54
-
55
- $event_data = array(
56
- 'NewUserID' => $user_id,
57
- 'UserChanger' => ! empty( $current_user ) ? $current_user->user_login : '',
58
- 'NewUserData' => (object) $new_user_data,
59
- 'EditUserLink' => add_query_arg( 'user_id', $user->ID, admin_url( 'user-edit.php' ) ),
60
- );
61
-
62
- $plugin->alerts->Trigger( $event, $event_data, true );
63
- }
64
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/Sensors/Register.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * User registration sensor file.
4
+ *
5
+ * @package wsal
6
+ * @subpackage sensors
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit; // Exit if accessed directly.
11
+ }
12
+
13
+ /**
14
+ * User registration sensor.
15
+ *
16
+ * 4000 New user was created on WordPress
17
+ *
18
+ * @package wsal
19
+ * @subpackage sensors
20
+ */
21
+ class WSAL_Sensors_Register extends WSAL_AbstractSensor {
22
+ /**
23
+ * {@inheritDoc}
24
+ */
25
+ public function HookEvents() {
26
+ /*
27
+ * Default WordPress registration utilizes action 'register_new_user', but we cannot rely on it to detect
28
+ * a front-end registration implemented by a third party. We hook into the action 'user_register' because it is
29
+ * part of the function 'wp_insert_user' that definitely runs.
30
+ */
31
+ add_action( 'user_register', array( $this, 'event_user_register' ), 10, 1 );
32
+ }
33
+
34
+ /**
35
+ * When a user registers, action 'user_register' is fired because it is part of the function 'wp_insert_user'. We
36
+ * can assume event 4000 if the current session is not logged in.
37
+ *
38
+ * @param int $user_id - User ID of the registered user.
39
+ */
40
+ public function event_user_register( $user_id ) {
41
+ if ( is_user_logged_in() ) {
42
+ // We bail if the user is logged in. That is no longer user registration, but user creation.
43
+ return;
44
+ }
45
+
46
+ $user = get_userdata( $user_id );
47
+ if ( ! $user instanceof WP_User ) {
48
+ // Bail if the user is not valid for some reason.
49
+ return;
50
+ }
51
+
52
+ $new_user_data = array(
53
+ 'Username' => $user->user_login,
54
+ 'Roles' => is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles,
55
+ );
56
+
57
+ $event_data = array(
58
+ 'NewUserID' => $user_id,
59
+ 'NewUserData' => (object) $new_user_data,
60
+ 'EditUserLink' => add_query_arg( 'user_id', $user_id, admin_url( 'user-edit.php' ) ),
61
+ );
62
+
63
+ if ( $this->IsMultisite() ) {
64
+ // Registration should not be logged on multisite if event 4024 is fired.
65
+ $this->plugin->alerts->TriggerIf(
66
+ 4000,
67
+ $event_data,
68
+ /**
69
+ * Don't log if event 4024 is fired.
70
+ *
71
+ * @param WSAL_AlertManager $mgr - Instance of WSAL_AlertManager.
72
+ */
73
+ function ( WSAL_AlertManager $mgr ) {
74
+ return ! $mgr->WillTrigger( 4013 );
75
+ }
76
+ );
77
+ } else {
78
+ $this->plugin->alerts->Trigger( 4000, $event_data, true );
79
+ }
80
+
81
+ }
82
+ }
classes/Sensors/System.php CHANGED
@@ -413,6 +413,24 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
413
  );
414
  }
415
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
416
  }
417
 
418
  /**
413
  );
414
  }
415
  }
416
+
417
+ // Site title.
418
+ if ( $is_option_page
419
+ && wp_verify_nonce( $post_array['_wpnonce'], 'general-options' )
420
+ && isset( $post_array['blogname'] ) ) {
421
+ $previous_value = get_option( 'blogname' );
422
+ $new_value = ( ! empty( $post_array['blogname'] ) ) ? $post_array['blogname'] : '';
423
+
424
+ if ( $previous_value !== $new_value ) {
425
+ $this->plugin->alerts->Trigger(
426
+ 6059,
427
+ array(
428
+ 'previous_value' => $previous_value,
429
+ 'new_value' => $new_value,
430
+ )
431
+ );
432
+ }
433
+ }
434
  }
435
 
436
  /**
classes/Sensors/UserProfile.php CHANGED
@@ -4,8 +4,10 @@
4
  *
5
  * User profile sensor file.
6
  *
7
- * @since 1.0.0
8
  * @package wsal
 
 
 
9
  */
10
 
11
  // Exit if accessed directly.
@@ -16,7 +18,6 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * User Profiles sensor.
18
  *
19
- * 4000 New user was created on WordPress
20
  * 4001 User created another WordPress user
21
  * 4002 The role of a user was changed by another WordPress user
22
  * 4003 User has changed his or her password
@@ -41,7 +42,7 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
41
  protected $old_superadmins;
42
 
43
  /**
44
- * Listening to events using WP hooks.
45
  */
46
  public function HookEvents() {
47
  add_action( 'profile_update', array( $this, 'event_user_updated' ), 10, 2 );
@@ -56,15 +57,22 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
56
  add_action( 'update_user_meta', array( $this, 'event_application_password_added' ), 10, 4 );
57
  add_action( 'retrieve_password', array( $this, 'event_password_reset_link_sent' ), 10, 1 );
58
 
 
 
59
  }
60
 
 
 
 
 
 
 
 
 
61
  public function event_application_password_added( $meta_id, $user_id, $meta_key, $_meta_value ) {
62
 
63
  // Filter global arrays for security.
64
- $post_array = filter_input_array( INPUT_POST );
65
- $get_array = filter_input_array( INPUT_GET );
66
  $server_array = filter_input_array( INPUT_SERVER );
67
-
68
  if ( ! isset( $server_array['HTTP_REFERER'] ) || ! isset( $server_array['REQUEST_URI'] ) ) {
69
  return;
70
  }
@@ -72,7 +80,7 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
72
  // Check the page which is performing this change.
73
  $referer_check = pathinfo( $server_array['HTTP_REFERER'] );
74
  $referer_check = $referer_check['filename'];
75
- $referer_check = ( strpos( $referer_check, '.' ) !== false ) ? strstr( $referer_check , '.', true ) : $referer_check;
76
 
77
  $is_correct_referer_and_action = false;
78
 
@@ -80,14 +88,17 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
80
  $is_correct_referer_and_action = true;
81
  }
82
 
83
- // Ensure we are dealign with the correct request.
84
- if ( $is_correct_referer_and_action && strpos( $server_array['REQUEST_URI'], '/wp/v2/users/'. $user_id .'/application-passwords' ) !== false ) {
85
 
86
  $old_value = get_user_meta( $user_id, '_application_passwords', true );
87
 
88
  $current_user = get_user_by( 'id', $user_id );
89
  $current_userdata = get_userdata( $user_id );
90
- $current_user_roles = implode( ', ', array_map( array( $this, 'filter_role_names' ), $current_userdata->roles ) );
 
 
 
91
  $event_id = ( 'user-edit' === $referer_check ) ? 4026 : 4025;
92
 
93
  // Note, firstname and lastname fields are purposefully spaces to avoid NULL.
@@ -100,19 +111,17 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
100
  'firstname' => ( empty( $current_user->user_firstname ) ) ? ' ' : $current_user->user_firstname,
101
  'lastname' => ( empty( $current_user->user_lastname ) ) ? ' ' : $current_user->user_lastname,
102
  'CurrentUserID' => $current_user->ID,
103
- 'friendly_name' => sanitize_text_field( $_POST['name'] ),
104
  'EventType' => 'added',
105
  )
106
  );
107
- }
108
-
109
- // Check if all have been removed.
110
- elseif ( ! empty( $old_value ) && count( $old_value ) > 1 && empty( $_meta_value ) ) {
111
- $event_id = ( 'user-edit' === $referer_check ) ? 4028 : 4027;
112
 
113
  // Note, firstname and lastname fields are purposefully spaces to avoid NULL.
114
  $this->plugin->alerts->Trigger(
115
- $event_id ,
116
  array(
117
  'roles' => $current_user_roles,
118
  'login' => $current_user->user_login,
@@ -122,10 +131,8 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
122
  'EventType' => 'revoked',
123
  )
124
  );
125
- }
126
-
127
- // Check the item that was removed.
128
- elseif ( count( $_meta_value ) < count( $old_value ) ) {
129
  $revoked_pw = array_diff( array_map( 'serialize', $old_value ), array_map( 'serialize', $_meta_value ) );
130
  $revoked_pw = array_values( array_map( 'unserialize', $revoked_pw ) );
131
  $revoked_pw_name = $revoked_pw[0]['name'];
@@ -133,7 +140,7 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
133
 
134
  // Note, firstname and lastname fields are purposefully spaces to avoid NULL.
135
  $this->plugin->alerts->Trigger(
136
- $event_id ,
137
  array(
138
  'roles' => $current_user_roles,
139
  'login' => $current_user->user_login,
@@ -146,14 +153,13 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
146
  );
147
  }
148
  }
149
-
150
  }
151
 
152
  /**
153
  * Method: Support for Ultimate Member email change
154
  * alert.
155
  *
156
- * @param int $user_id - User ID.
157
  * @param WP_User $old_userdata - Old WP_User object.
158
  */
159
  public function event_user_updated( $user_id, $old_userdata ) {
@@ -215,6 +221,23 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
215
  );
216
  }
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  // Alert if role has changed via Members plugin.
219
  if ( isset( $_POST['members_user_roles'] ) && ! empty( $_POST['members_user_roles'] ) ) {
220
  if ( $old_userdata->roles !== $_POST['members_user_roles'] ) {
@@ -226,9 +249,10 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
226
  /**
227
  * Triggered when a user role is changed.
228
  *
229
- * @param int $user_id - User ID of the user.
230
- * @param string $new_role - New role.
231
- * @param array $old_roles - Array of old roles.
 
232
  */
233
  public function event_user_role_changed( $user_id, $new_role, $old_roles, $use_posted_data = false ) {
234
  // Get WP_User object.
@@ -331,9 +355,9 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
331
  *
332
  * Triggers when a user is granted super admin access.
333
  *
334
- * @since 3.4
335
- *
336
  * @param int $user_id - ID of the user that was granted Super Admin privileges.
 
 
337
  */
338
  public function event_super_access_granted( $user_id ) {
339
  $user = get_userdata( $user_id );
@@ -357,9 +381,9 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
357
  *
358
  * Triggers when a user is revoked super admin access.
359
  *
360
- * @since 3.4
361
- *
362
  * @param int $user_id - ID of the user that was revoked Super Admin privileges.
 
 
363
  */
364
  public function event_super_access_revoked( $user_id ) {
365
  $user = get_userdata( $user_id );
@@ -384,10 +408,13 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
384
  * @param string $user_login User's login name.
385
  */
386
  public function event_password_reset_link_sent( $user_login ) {
387
- $current_user = get_user_by( 'login', $user_login );
388
 
389
  $current_userdata = get_userdata( $current_user->ID );
390
- $current_user_roles = implode( ', ', array_map( array( $this, 'filter_role_names' ), $current_userdata->roles ) );
 
 
 
391
 
392
  $this->plugin->alerts->Trigger(
393
  4029,
@@ -405,13 +432,14 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
405
  /**
406
  * Remove BBPress Prefix from User Role.
407
  *
408
- * @since 3.4
409
- *
410
  * @param string $user_role - User role.
 
411
  * @return string
 
412
  */
413
  public function filter_role_names( $user_role ) {
414
  global $wp_roles;
 
415
  return isset( $wp_roles->role_names[ $user_role ] ) ? $wp_roles->role_names[ $user_role ] : false;
416
  }
417
 
@@ -438,4 +466,48 @@ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
438
  || $mgr->WillOrHasTriggered( 4001 )
439
  );
440
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
  }
4
  *
5
  * User profile sensor file.
6
  *
 
7
  * @package wsal
8
+ * @subpackage sensors
9
+ *
10
+ * @since 1.0.0
11
  */
12
 
13
  // Exit if accessed directly.
18
  /**
19
  * User Profiles sensor.
20
  *
 
21
  * 4001 User created another WordPress user
22
  * 4002 The role of a user was changed by another WordPress user
23
  * 4003 User has changed his or her password
42
  protected $old_superadmins;
43
 
44
  /**
45
+ * {@inheritDoc}
46
  */
47
  public function HookEvents() {
48
  add_action( 'profile_update', array( $this, 'event_user_updated' ), 10, 2 );
57
  add_action( 'update_user_meta', array( $this, 'event_application_password_added' ), 10, 4 );
58
  add_action( 'retrieve_password', array( $this, 'event_password_reset_link_sent' ), 10, 1 );
59
 
60
+ // We hook into action 'user_register' because it is part of the function 'wp_insert_user'.
61
+ add_action( 'user_register', array( $this, 'on_user_register' ), 10, 1 );
62
  }
63
 
64
+ /**
65
+ * Captures addition of application passwords.
66
+ *
67
+ * @param int $meta_id ID of the metadata entry to update.
68
+ * @param int $user_id ID of the user metadata is for.
69
+ * @param string $meta_key Metadata key.
70
+ * @param mixed $_meta_value Metadata value. Serialized if non-scalar.
71
+ */
72
  public function event_application_password_added( $meta_id, $user_id, $meta_key, $_meta_value ) {
73
 
74
  // Filter global arrays for security.
 
 
75
  $server_array = filter_input_array( INPUT_SERVER );
 
76
  if ( ! isset( $server_array['HTTP_REFERER'] ) || ! isset( $server_array['REQUEST_URI'] ) ) {
77
  return;
78
  }
80
  // Check the page which is performing this change.
81
  $referer_check = pathinfo( $server_array['HTTP_REFERER'] );
82
  $referer_check = $referer_check['filename'];
83
+ $referer_check = ( strpos( $referer_check, '.' ) !== false ) ? strstr( $referer_check, '.', true ) : $referer_check;
84
 
85
  $is_correct_referer_and_action = false;
86
 
88
  $is_correct_referer_and_action = true;
89
  }
90
 
91
+ // Ensure we are dealing with the correct request.
92
+ if ( $is_correct_referer_and_action && strpos( $server_array['REQUEST_URI'], '/wp/v2/users/' . $user_id . '/application-passwords' ) !== false ) {
93
 
94
  $old_value = get_user_meta( $user_id, '_application_passwords', true );
95
 
96
  $current_user = get_user_by( 'id', $user_id );
97
  $current_userdata = get_userdata( $user_id );
98
+ $current_user_roles = implode( ', ', array_map( array(
99
+ $this,
100
+ 'filter_role_names'
101
+ ), $current_userdata->roles ) );
102
  $event_id = ( 'user-edit' === $referer_check ) ? 4026 : 4025;
103
 
104
  // Note, firstname and lastname fields are purposefully spaces to avoid NULL.
111
  'firstname' => ( empty( $current_user->user_firstname ) ) ? ' ' : $current_user->user_firstname,
112
  'lastname' => ( empty( $current_user->user_lastname ) ) ? ' ' : $current_user->user_lastname,
113
  'CurrentUserID' => $current_user->ID,
114
+ 'friendly_name' => sanitize_text_field( wp_unslash( $_POST['name'] ) ),
115
  'EventType' => 'added',
116
  )
117
  );
118
+ } elseif ( ! empty( $old_value ) && count( $old_value ) > 1 && empty( $_meta_value ) ) {
119
+ // Check if all have been removed.
120
+ $event_id = ( 'user-edit' === $referer_check ) ? 4028 : 4027;
 
 
121
 
122
  // Note, firstname and lastname fields are purposefully spaces to avoid NULL.
123
  $this->plugin->alerts->Trigger(
124
+ $event_id,
125
  array(
126
  'roles' => $current_user_roles,
127
  'login' => $current_user->user_login,
131
  'EventType' => 'revoked',
132
  )
133
  );
134
+ } elseif ( count( $_meta_value ) < count( $old_value ) ) {
135
+ // Check the item that was removed.
 
 
136
  $revoked_pw = array_diff( array_map( 'serialize', $old_value ), array_map( 'serialize', $_meta_value ) );
137
  $revoked_pw = array_values( array_map( 'unserialize', $revoked_pw ) );
138
  $revoked_pw_name = $revoked_pw[0]['name'];
140
 
141
  // Note, firstname and lastname fields are purposefully spaces to avoid NULL.
142
  $this->plugin->alerts->Trigger(
143
+ $event_id,
144
  array(
145
  'roles' => $current_user_roles,
146
  'login' => $current_user->user_login,
153
  );
154
  }
155
  }
 
156
  }
157
 
158
  /**
159
  * Method: Support for Ultimate Member email change
160
  * alert.
161
  *
162
+ * @param int $user_id - User ID.
163
  * @param WP_User $old_userdata - Old WP_User object.
164
  */
165
  public function event_user_updated( $user_id, $old_userdata ) {
221
  );
222
  }
223
 
224
+ // Alert if website URL is changed.
225
+ if ( $old_userdata->user_url !== $new_userdata->user_url ) {
226
+ $user_roles = implode( ', ', array_map( array( $this, 'filter_role_names' ), $new_userdata->roles ) );
227
+ $this->plugin->alerts->Trigger(
228
+ 4021,
229
+ array(
230
+ 'TargetUsername' => $new_userdata->user_login,
231
+ 'old_url' => $old_userdata->user_url,
232
+ 'new_url' => $new_userdata->user_url,
233
+ 'Roles' => $user_roles,
234
+ 'FirstName' => $new_userdata->user_firstname,
235
+ 'LastName' => $new_userdata->user_lastname,
236
+ 'EditUserLink' => add_query_arg( 'user_id', $user_id, admin_url( 'user-edit.php' ) ),
237
+ )
238
+ );
239
+ }
240
+
241
  // Alert if role has changed via Members plugin.
242
  if ( isset( $_POST['members_user_roles'] ) && ! empty( $_POST['members_user_roles'] ) ) {
243
  if ( $old_userdata->roles !== $_POST['members_user_roles'] ) {
249
  /**
250
  * Triggered when a user role is changed.
251
  *
252
+ * @param int $user_id User ID of the user.
253
+ * @param string $new_role New role.
254
+ * @param array $old_roles Array of old roles.
255
+ * @param boolean $use_posted_data If true, posted user data is used.
256
  */
257
  public function event_user_role_changed( $user_id, $new_role, $old_roles, $use_posted_data = false ) {
258
  // Get WP_User object.
355
  *
356
  * Triggers when a user is granted super admin access.
357
  *
 
 
358
  * @param int $user_id - ID of the user that was granted Super Admin privileges.
359
+ *
360
+ * @since 3.4
361
  */
362
  public function event_super_access_granted( $user_id ) {
363
  $user = get_userdata( $user_id );
381
  *
382
  * Triggers when a user is revoked super admin access.
383
  *
 
 
384
  * @param int $user_id - ID of the user that was revoked Super Admin privileges.
385
+ *
386
+ * @since 3.4
387
  */
388
  public function event_super_access_revoked( $user_id ) {
389
  $user = get_userdata( $user_id );
408
  * @param string $user_login User's login name.
409
  */
410
  public function event_password_reset_link_sent( $user_login ) {
411
+ $current_user = get_user_by( 'login', $user_login );
412
 
413
  $current_userdata = get_userdata( $current_user->ID );
414
+ $current_user_roles = implode( ', ', array_map( array(
415
+ $this,
416
+ 'filter_role_names'
417
+ ), $current_userdata->roles ) );
418
 
419
  $this->plugin->alerts->Trigger(
420
  4029,
432
  /**
433
  * Remove BBPress Prefix from User Role.
434
  *
 
 
435
  * @param string $user_role - User role.
436
+ *
437
  * @return string
438
+ * @since 3.4
439
  */
440
  public function filter_role_names( $user_role ) {
441
  global $wp_roles;
442
+
443
  return isset( $wp_roles->role_names[ $user_role ] ) ? $wp_roles->role_names[ $user_role ] : false;
444
  }
445
 
466
  || $mgr->WillOrHasTriggered( 4001 )
467
  );
468
  }
469
+
470
+ /**
471
+ * When a user is created (by any means other than direct database insert), action 'user_register' is fired because
472
+ * it is part of the function 'wp_insert_user'. We can assume one of the following events if the current session is
473
+ * logged in end event 4000 is not triggerred from elsewhere (it is also hooked into action 'user_register').
474
+ *
475
+ * - 4001 User created another WordPress user
476
+ * - 4012 New network user created
477
+ *
478
+ * @param int $user_id - User ID of the registered user.
479
+ */
480
+ public function on_user_register( $user_id ) {
481
+ if ( ! is_user_logged_in() ) {
482
+ // We bail if the user is not logged in. That is no longer user creation, but a user registration.
483
+ return;
484
+ }
485
+
486
+ $user = get_userdata( $user_id );
487
+ if ( ! $user instanceof WP_User ) {
488
+ // Bail if the user is not valid for some reason.
489
+ return;
490
+ }
491
+
492
+ $new_user_data = array(
493
+ 'Username' => $user->user_login,
494
+ 'FirstName' => ! empty( $user->user_firstname ) ? $user->user_firstname : '',
495
+ 'LastName' => ! empty( $user->user_lastname ) ? $user->user_lastname : '',
496
+ );
497
+
498
+ $event_code = $this->plugin->IsMultisite() ? 4012 : 4001;
499
+ if ( 4001 === $event_code ) {
500
+ $new_user_data['Roles'] = is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles;
501
+ } else if ( 4012 === $event_code ) {
502
+ $new_user_data['Email'] = $user->user_email;
503
+ }
504
+
505
+ $event_data = array(
506
+ 'NewUserID' => $user_id,
507
+ 'NewUserData' => (object) $new_user_data,
508
+ 'EditUserLink' => add_query_arg( 'user_id', $user_id, admin_url( 'user-edit.php' ) ),
509
+ );
510
+
511
+ $this->plugin->alerts->Trigger( $event_code, $event_data, $this->plugin->IsMultisite() );
512
+ }
513
  }
classes/Settings.php CHANGED
@@ -202,7 +202,7 @@ class WSAL_Settings {
202
  * @param boolean $newvalue - True if enabled.
203
  */
204
  public function set_admin_bar_notif( $newvalue ) {
205
- $this->_plugin->SetGlobalBooleanSetting( 'disable-admin-bar-notif', ! $newvalue );
206
  }
207
 
208
  /**
@@ -224,7 +224,7 @@ class WSAL_Settings {
224
  * @param string $newvalue - New option value.
225
  */
226
  public function set_admin_bar_notif_updates( $newvalue ) {
227
- $this->_plugin->SetGlobalSetting( 'admin-bar-notif-updates', $newvalue );
228
  }
229
 
230
  /**
@@ -484,6 +484,11 @@ class WSAL_Settings {
484
  return $this->_plugin->GetGlobalBooleanSetting( 'delete-data' );
485
  }
486
 
 
 
 
 
 
487
  public function SetDeleteData( $enabled ) {
488
  $this->_plugin->SetGlobalBooleanSetting( 'delete-data', $enabled );
489
  }
@@ -528,7 +533,7 @@ class WSAL_Settings {
528
 
529
 
530
  $this->_viewers = $users_or_roles;
531
- $this->_plugin->SetGlobalSetting( 'plugin-viewers', implode( ',', $this->_viewers ) );
532
  }
533
 
534
  /**
@@ -558,7 +563,7 @@ class WSAL_Settings {
558
  $this->_plugin->alerts->Trigger( 6049, $alert_data );
559
  }
560
 
561
- $this->_plugin->SetGlobalSetting( 'restrict-plugin-settings', $setting );
562
  }
563
 
564
  /**
@@ -586,7 +591,7 @@ class WSAL_Settings {
586
  * @since 4.1.3
587
  */
588
  public function set_restrict_log_viewer( $setting ) {
589
- $this->_plugin->SetGlobalSetting( 'restrict-log-viewer', $setting );
590
  }
591
 
592
  public function SetViewPerPage( $newvalue ) {
@@ -1471,10 +1476,11 @@ class WSAL_Settings {
1471
  /**
1472
  * Get the log limit for failed login attempts.
1473
  *
 
1474
  * @since 2.6.3
1475
  */
1476
  public function get_failed_login_limit() {
1477
- return $this->_plugin->GetGlobalSetting( 'log-failed-login-limit', 10 );
1478
  }
1479
 
1480
  /**
@@ -1494,10 +1500,11 @@ class WSAL_Settings {
1494
  /**
1495
  * Get the log limit for failed login attempts for visitor.
1496
  *
 
1497
  * @since 2.6.3
1498
  */
1499
  public function get_visitor_failed_login_limit() {
1500
- return $this->_plugin->GetGlobalSetting( 'log-visitor-failed-login-limit', 10 );
1501
  }
1502
 
1503
 
@@ -1592,8 +1599,8 @@ class WSAL_Settings {
1592
  ) {
1593
  // Check if freemius state is anonymous.
1594
  if ( ! wsal_freemius()->is_premium() && 'anonymous' === $this->_plugin->GetGlobalSetting( 'freemius_state', 'anonymous' ) ) {
1595
- // Update freemius state to skipped.
1596
- $this->_plugin->SetGlobalSetting( 'wsal_freemius_state', 'skipped' );
1597
 
1598
  if ( ! $this->_plugin->IsMultisite() ) {
1599
  wsal_freemius()->skip_connection(); // Opt out.
@@ -1613,7 +1620,7 @@ class WSAL_Settings {
1613
  $this->SetIncognito( true ); // Incognito mode to hide WSAL on plugins page.
1614
  $this->set_restrict_log_viewer( 'only_me' );
1615
  $this->set_restrict_plugin_setting( 'only_me' );
1616
- // current user with fallback to default admin (in case this is triggered using WP CLI or something similar)
1617
  $only_me_user_id = is_user_logged_in() ? get_current_user_id() : 1;
1618
  $this->set_only_me_user_id( $only_me_user_id );
1619
  $this->_plugin->SetGlobalBooleanSetting( 'mwp-child-stealth-mode', true ); // Save stealth mode option.
@@ -1759,7 +1766,7 @@ class WSAL_Settings {
1759
  * @return boolean
1760
  */
1761
  public function is_infinite_scroll() {
1762
- return 'infinite-scroll' === $this->get_events_type_nav() ? true : false;
1763
  }
1764
 
1765
  /**
@@ -1783,7 +1790,6 @@ class WSAL_Settings {
1783
  * @since 3.3.1.1
1784
  *
1785
  * @param string $nav_type - Navigation type.
1786
- * @return string
1787
  */
1788
  public function set_events_type_nav( $nav_type ) {
1789
  $this->_plugin->SetGlobalSetting( 'events-nav-type', $nav_type );
@@ -1796,8 +1802,7 @@ class WSAL_Settings {
1796
  */
1797
  public function get_plugin_settings() {
1798
  global $wpdb;
1799
- $plugin_options = $wpdb->get_results( "SELECT * FROM $wpdb->options WHERE option_name LIKE 'wsal_%'" );
1800
- return $plugin_options;
1801
  }
1802
 
1803
  /**
@@ -1996,9 +2001,7 @@ class WSAL_Settings {
1996
  );
1997
 
1998
  // Get the option.
1999
- $value = \WSAL\Helpers\Options::get_option_value_ignore_prefix( self::FRONT_END_EVENTS_OPTION_NAME, $default );
2000
-
2001
- return $value;
2002
  }
2003
 
2004
  /**
@@ -2008,17 +2011,17 @@ class WSAL_Settings {
2008
  * @return bool
2009
  */
2010
  public static function set_frontend_events( $value = array() ) {
2011
- return \WSAL\Helpers\Options::set_option_value_ignore_prefix( self::FRONT_END_EVENTS_OPTION_NAME, $value );
2012
  }
2013
 
2014
  /**
2015
  * Stores the ID of user who restricted the plugin settings access to "only me".
2016
  *
2017
- * @param int $user_id
2018
  * @since 4.1.3
2019
  */
2020
  public function set_only_me_user_id( $user_id ) {
2021
- $this->_plugin->SetGlobalSetting( 'only-me-user-id', $user_id );
2022
  }
2023
 
2024
  /**
@@ -2066,18 +2069,18 @@ class WSAL_Settings {
2066
  }
2067
 
2068
  public function determine_added_and_removed_items( $old_value, $value ) {
2069
- $old_value = ( ! is_array( $old_value ) ) ? explode( ',', $old_value ) : $old_value;
2070
- $value = ( ! is_array( $value ) ) ? explode( ',', $value ) : $value;
2071
- $return = [];
2072
- $return[ 'removed' ] = array_filter( array_diff( $old_value, $value ) );
2073
- $return[ 'added' ] = array_filter( array_diff( $value, $old_value ) );
2074
 
2075
  return $return;
2076
  }
2077
 
2078
  public function tidy_blank_values( $value ) {
2079
  return ( empty( $value ) ) ? __( 'None provided', 'wp-security-audit-log' ) : $value;
2080
- }
2081
 
2082
  /**
2083
  * Retrieves current database version.
@@ -2096,6 +2099,7 @@ class WSAL_Settings {
2096
  * @since 4.3.2
2097
  */
2098
  public function set_database_version( $version ) {
2099
- $this->_plugin->SetGlobalSetting( 'db_version', $version );
2100
  }
 
2101
  }
202
  * @param boolean $newvalue - True if enabled.
203
  */
204
  public function set_admin_bar_notif( $newvalue ) {
205
+ $this->_plugin->SetGlobalBooleanSetting( 'disable-admin-bar-notif', ! $newvalue, true );
206
  }
207
 
208
  /**
224
  * @param string $newvalue - New option value.
225
  */
226
  public function set_admin_bar_notif_updates( $newvalue ) {
227
+ $this->_plugin->SetGlobalSetting( 'admin-bar-notif-updates', $newvalue, true );
228
  }
229
 
230
  /**
484
  return $this->_plugin->GetGlobalBooleanSetting( 'delete-data' );
485
  }
486
 
487
+ /**
488
+ * Sets the plugin setting that allows data deletion on plugin uninstall.
489
+ *
490
+ * @param mixed $enabled
491
+ */
492
  public function SetDeleteData( $enabled ) {
493
  $this->_plugin->SetGlobalBooleanSetting( 'delete-data', $enabled );
494
  }
533
 
534
 
535
  $this->_viewers = $users_or_roles;
536
+ $this->_plugin->SetGlobalSetting( 'plugin-viewers', implode( ',', $this->_viewers ), true );
537
  }
538
 
539
  /**
563
  $this->_plugin->alerts->Trigger( 6049, $alert_data );
564
  }
565
 
566
+ $this->_plugin->SetGlobalSetting( 'restrict-plugin-settings', $setting, true );
567
  }
568
 
569
  /**
591
  * @since 4.1.3
592
  */
593
  public function set_restrict_log_viewer( $setting ) {
594
+ $this->_plugin->SetGlobalSetting( 'restrict-log-viewer', $setting, true );
595
  }
596
 
597
  public function SetViewPerPage( $newvalue ) {
1476
  /**
1477
  * Get the log limit for failed login attempts.
1478
  *
1479
+ * @return int
1480
  * @since 2.6.3
1481
  */
1482
  public function get_failed_login_limit() {
1483
+ return intval($this->_plugin->GetGlobalSetting( 'log-failed-login-limit', 10 ));
1484
  }
1485
 
1486
  /**
1500
  /**
1501
  * Get the log limit for failed login attempts for visitor.
1502
  *
1503
+ * @return int
1504
  * @since 2.6.3
1505
  */
1506
  public function get_visitor_failed_login_limit() {
1507
+ return intval( $this->_plugin->GetGlobalSetting( 'log-visitor-failed-login-limit', 10 ) );
1508
  }
1509
 
1510
 
1599
  ) {
1600
  // Check if freemius state is anonymous.
1601
  if ( ! wsal_freemius()->is_premium() && 'anonymous' === $this->_plugin->GetGlobalSetting( 'freemius_state', 'anonymous' ) ) {
1602
+ // Update Freemius state to skipped.
1603
+ $this->_plugin->SetGlobalSetting( 'wsal_freemius_state', 'skipped', true );
1604
 
1605
  if ( ! $this->_plugin->IsMultisite() ) {
1606
  wsal_freemius()->skip_connection(); // Opt out.
1620
  $this->SetIncognito( true ); // Incognito mode to hide WSAL on plugins page.
1621
  $this->set_restrict_log_viewer( 'only_me' );
1622
  $this->set_restrict_plugin_setting( 'only_me' );
1623
+ // Current user with fallback to default admin (in case this is triggered using WP CLI or something similar).
1624
  $only_me_user_id = is_user_logged_in() ? get_current_user_id() : 1;
1625
  $this->set_only_me_user_id( $only_me_user_id );
1626
  $this->_plugin->SetGlobalBooleanSetting( 'mwp-child-stealth-mode', true ); // Save stealth mode option.
1766
  * @return boolean
1767
  */
1768
  public function is_infinite_scroll() {
1769
+ return 'infinite-scroll' === $this->get_events_type_nav();
1770
  }
1771
 
1772
  /**
1790
  * @since 3.3.1.1
1791
  *
1792
  * @param string $nav_type - Navigation type.
 
1793
  */
1794
  public function set_events_type_nav( $nav_type ) {
1795
  $this->_plugin->SetGlobalSetting( 'events-nav-type', $nav_type );
1802
  */
1803
  public function get_plugin_settings() {
1804
  global $wpdb;
1805
+ return $wpdb->get_results( "SELECT * FROM $wpdb->options WHERE option_name LIKE 'wsal_%'" );
 
1806
  }
1807
 
1808
  /**
2001
  );
2002
 
2003
  // Get the option.
2004
+ return \WSAL\Helpers\Options::get_option_value_ignore_prefix( self::FRONT_END_EVENTS_OPTION_NAME, $default );
 
 
2005
  }
2006
 
2007
  /**
2011
  * @return bool
2012
  */
2013
  public static function set_frontend_events( $value = array() ) {
2014
+ return \WSAL\Helpers\Options::set_option_value_ignore_prefix( self::FRONT_END_EVENTS_OPTION_NAME, $value, true );
2015
  }
2016
 
2017
  /**
2018
  * Stores the ID of user who restricted the plugin settings access to "only me".
2019
  *
2020
+ * @param int $user_id User ID.
2021
  * @since 4.1.3
2022
  */
2023
  public function set_only_me_user_id( $user_id ) {
2024
+ $this->_plugin->SetGlobalSetting( 'only-me-user-id', $user_id, true );
2025
  }
2026
 
2027
  /**
2069
  }
2070
 
2071
  public function determine_added_and_removed_items( $old_value, $value ) {
2072
+ $old_value = ( ! is_array( $old_value ) ) ? explode( ',', $old_value ) : $old_value;
2073
+ $value = ( ! is_array( $value ) ) ? explode( ',', $value ) : $value;
2074
+ $return = array();
2075
+ $return['removed'] = array_filter( array_diff( $old_value, $value ) );
2076
+ $return['added'] = array_filter( array_diff( $value, $old_value ) );
2077
 
2078
  return $return;
2079
  }
2080
 
2081
  public function tidy_blank_values( $value ) {
2082
  return ( empty( $value ) ) ? __( 'None provided', 'wp-security-audit-log' ) : $value;
2083
+ }
2084
 
2085
  /**
2086
  * Retrieves current database version.
2099
  * @since 4.3.2
2100
  */
2101
  public function set_database_version( $version ) {
2102
+ $this->_plugin->SetGlobalSetting( 'db_version', $version, true );
2103
  }
2104
+
2105
  }
classes/ThirdPartyExtensions/AbstractExtension.php CHANGED
@@ -104,5 +104,13 @@ if ( ! class_exists( 'WSAL_AbstractExtension' ) ) {
104
 
105
  abstract public function get_color();
106
 
 
 
 
 
 
 
 
 
107
  }
108
  }
104
 
105
  abstract public function get_color();
106
 
107
+ /**
108
+ * Gets the filename of the plugin this extension is targetting.
109
+ *
110
+ * @return string Filename.
111
+ *
112
+ * @since 4.4.0
113
+ */
114
+ abstract public function get_plugin_filename();
115
  }
116
  }
classes/ThirdPartyExtensions/BBPressExtension.php CHANGED
@@ -10,12 +10,12 @@ if ( ! class_exists( 'WSAL_BBPressExtension' ) ) {
10
  'addon_for' => 'bbpress',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'bbpress.png',
13
- 'plugin_slug' => 'wp-security-audit-log-add-on-for-bbpress/wsal-bbpress.php',
14
  'plugin_basename' => 'wsal-bbpress.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/wp-security-audit-log-add-on-for-bbpress.latest-stable.zip',
16
- 'event_tab_id' => '#tab-bbpress-forums',
17
  'plugin_description' => 'Keep a log of your sites bbPress activity, from forum and topic creation, user profile changes and more.',
18
- )
19
  );
20
 
21
  // combine the two arrays.
@@ -35,7 +35,7 @@ if ( ! class_exists( 'WSAL_BBPressExtension' ) ) {
35
  }
36
 
37
  public function get_custom_post_types() {
38
- return [ 'forum', 'topic', 'reply' ];
39
  }
40
 
41
  public function get_plugin_name() {
@@ -49,5 +49,9 @@ if ( ! class_exists( 'WSAL_BBPressExtension' ) ) {
49
  public function get_color() {
50
  return '#8dc770';
51
  }
 
 
 
 
52
  }
53
  }
10
  'addon_for' => 'bbpress',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'bbpress.png',
13
+ 'plugin_slug' => $this->get_plugin_filename(),
14
  'plugin_basename' => 'wsal-bbpress.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/wp-security-audit-log-add-on-for-bbpress.latest-stable.zip',
16
+ 'event_tab_id' => '#cat-bbpress-forums',
17
  'plugin_description' => 'Keep a log of your sites bbPress activity, from forum and topic creation, user profile changes and more.',
18
+ ),
19
  );
20
 
21
  // combine the two arrays.
35
  }
36
 
37
  public function get_custom_post_types() {
38
+ return array( 'forum', 'topic', 'reply' );
39
  }
40
 
41
  public function get_plugin_name() {
49
  public function get_color() {
50
  return '#8dc770';
51
  }
52
+
53
+ public function get_plugin_filename() {
54
+ return 'wp-security-audit-log-add-on-for-bbpress/wsal-bbpress.php';
55
+ }
56
  }
57
  }
classes/ThirdPartyExtensions/GravityFormsExtension.php CHANGED
@@ -10,9 +10,9 @@ if ( ! class_exists( 'WSAL_GravityFormsExtension' ) ) {
10
  'addon_for' => 'gravityforms',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'gravityforms.png',
13
- 'plugin_slug' => 'activity-log-gravity-forms/activity-log-gravity-forms.php',
14
  'plugin_url' => 'https://downloads.wordpress.org/plugin/activity-log-gravity-forms.latest-stable.zip',
15
- 'event_tab_id' => '#tab-gravity-forms',
16
  'plugin_description' => __( 'Keep a record of when someone adds, modifies or deletes forms, entries and more in the Gravity Forms plugin.', 'wp-security-audit-log' ),
17
  )
18
  );
@@ -44,5 +44,9 @@ if ( ! class_exists( 'WSAL_GravityFormsExtension' ) ) {
44
  public function get_color() {
45
  return '#F15A29';
46
  }
 
 
 
 
47
  }
48
  }
10
  'addon_for' => 'gravityforms',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'gravityforms.png',
13
+ 'plugin_slug' => $this->get_plugin_filename(),
14
  'plugin_url' => 'https://downloads.wordpress.org/plugin/activity-log-gravity-forms.latest-stable.zip',
15
+ 'event_tab_id' => '#cat-gravity-forms',
16
  'plugin_description' => __( 'Keep a record of when someone adds, modifies or deletes forms, entries and more in the Gravity Forms plugin.', 'wp-security-audit-log' ),
17
  )
18
  );
44
  public function get_color() {
45
  return '#F15A29';
46
  }
47
+
48
+ public function get_plugin_filename() {
49
+ return 'activity-log-gravity-forms/activity-log-gravity-forms.php';
50
+ }
51
  }
52
  }
classes/ThirdPartyExtensions/TablePressExtension.php CHANGED
@@ -10,12 +10,12 @@ if ( ! class_exists( 'WSAL_TablePressExtension' ) ) {
10
  'addon_for' => 'tablepress',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'tablepress.png',
13
- 'plugin_slug' => 'tablepress/tablepress.php',
14
  'plugin_basename' => 'tablepress.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/activity-log-tablepress.latest-stable.zip',
16
- 'event_tab_id' => '#tab-tablepress',
17
  'plugin_description' => 'Keep a log of all the changes in your TablePress tables.',
18
- )
19
  );
20
 
21
  // combine the two arrays.
@@ -35,7 +35,7 @@ if ( ! class_exists( 'WSAL_TablePressExtension' ) ) {
35
  }
36
 
37
  public function get_custom_post_types() {
38
- return [ 'tablepress_table' ];
39
  }
40
 
41
  public function get_plugin_name() {
@@ -43,11 +43,15 @@ if ( ! class_exists( 'WSAL_TablePressExtension' ) ) {
43
  }
44
 
45
  public function get_plugin_icon_url() {
46
- return 'https://ps.w.org/activity-log-wp-seo/assets/icon-128x128.png?rev=2393849';
47
  }
48
 
49
  public function get_color() {
50
  return '#a4286a';
51
  }
 
 
 
 
52
  }
53
  }
10
  'addon_for' => 'tablepress',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'tablepress.png',
13
+ 'plugin_slug' => $this->get_plugin_filename(),
14
  'plugin_basename' => 'tablepress.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/activity-log-tablepress.latest-stable.zip',
16
+ 'event_tab_id' => '#cat-tablepress',
17
  'plugin_description' => 'Keep a log of all the changes in your TablePress tables.',
18
+ ),
19
  );
20
 
21
  // combine the two arrays.
35
  }
36
 
37
  public function get_custom_post_types() {
38
+ return array( 'tablepress_table' );
39
  }
40
 
41
  public function get_plugin_name() {
43
  }
44
 
45
  public function get_plugin_icon_url() {
46
+ return 'https://ps.w.org/activity-log-tablepress/assets/icon-128x128.png?rev=2393849';
47
  }
48
 
49
  public function get_color() {
50
  return '#a4286a';
51
  }
52
+
53
+ public function get_plugin_filename() {
54
+ return 'activity-log-tablepress/wsal-tablepress.php';
55
+ }
56
  }
57
  }
classes/ThirdPartyExtensions/WFCMExtension.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'WSAL_WFCMExtension' ) ) {
4
+
5
+ class WSAL_WFCMExtension extends WSAL_AbstractExtension {
6
+
7
+ public function __construct() {
8
+ add_filter( 'wsal_append_dailynotification_email_content', [ $this, 'append_dailynotification_email_content' ], 10, 2 );
9
+ }
10
+
11
+ public function filter_installable_plugins( $plugins ) {
12
+ $new_plugin = array(
13
+ array(
14
+ 'addon_for' => 'wfcm',
15
+ 'title' => $this->get_plugin_name(),
16
+ 'image_filename' => 'wfcm.png',
17
+ 'plugin_slug' => $this->get_plugin_filename(),
18
+ 'plugin_basename' => 'website-file-changes-monitor.php',
19
+ 'plugin_url' => 'https://downloads.wordpress.org/plugin/website-file-changes-monitor.latest-stable.zip',
20
+ 'event_tab_id' => '#cat-wfcm',
21
+ 'plugin_description' => 'To keep a log of file changes please install Website File Changes Monitor, a plugin which is also developed by us.',
22
+ )
23
+ );
24
+
25
+ // combine the two arrays.
26
+ return array_merge( $plugins, $new_plugin );
27
+ }
28
+
29
+ public function add_event_codes( $addon_event_codes ) {
30
+ $new_event_codes = array(
31
+ 'wfcm' => array(
32
+ 'name' => $this->get_plugin_name(),
33
+ 'event_ids' => array( 6028, 6029, 6030, 6031, 6032, 6033 ),
34
+ ),
35
+ );
36
+
37
+ // combine the two arrays.
38
+ return array_merge( $addon_event_codes, $new_event_codes );
39
+ }
40
+
41
+ public function get_plugin_name() {
42
+ return 'Website File Changes Monitor';
43
+ }
44
+
45
+ public function get_plugin_icon_url() {
46
+ return 'https://ps.w.org/website-file-changes-monitor/assets/icon-128x128.png?rev=2393849';
47
+ }
48
+
49
+ public function get_color() {
50
+ return '#a4286a';
51
+ }
52
+
53
+ public function append_dailynotification_email_content( $body, $events ) {
54
+
55
+ if ( ! empty( $events ) ) {
56
+ foreach ( $events as $event ) {
57
+ if ( 6028 === $event->alert_id ) {
58
+ $files_modified[] = $event;
59
+ } elseif ( 6029 === $event->alert_id ) {
60
+ $files_added[] = $event;
61
+ } elseif ( 6030 === $event->alert_id ) {
62
+ $files_deleted[] = $event;
63
+ }
64
+ }
65
+ }
66
+
67
+ if ( class_exists( 'Website_File_Changes_Monitor' ) ) {
68
+ // File changes.
69
+ if ( ! empty( $files_added ) || ! empty( $files_modified ) || ! empty( $files_deleted ) ) {
70
+ $body .= '<!-- Website File Changes Start --><tr><td><table width="100%" cellpadding="0" cellspacing="0" border="0"><!-- Title Start --><tr><td style="font-family: Verdana, sans-serif; font-weight: bold; font-size: 20px; line-height: 28px; color: #404040; text-align: left; padding-bottom: 13px;">Website File Changes</td></tr><!-- Title End --><!-- Desc Start --><!-- Table Border Start --><tr><td style="padding-bottom: 40px;"><table width="100%" cellpadding="0" cellspacing="0" border="0">';
71
+
72
+ $body .= '<tr><td style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #404040;padding-bottom: 20px;">During the last file integrity scan on '. date( 'd/m/Y', get_option( 'wfcm_last-scan-timestamp' ) ) .' at '. date( 'H:i:s', get_option( 'wfcm_last-scan-timestamp' ) ) .' we detected the following file changes:</td></tr>';
73
+
74
+ if ( ! empty( $files_added ) ) {
75
+ $body .= '<tr><td style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #404040;padding-bottom: 5px;"><img src="' . $this->media['check'] . '" style="height: 14px; width: 14px; position: relative; top: 1px;" /> New files identified</td></tr>';
76
+ }
77
+
78
+ if ( ! empty( $files_modified ) ) {
79
+ $body .= '<tr><td style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #404040;padding-bottom: 5px;padding-top: 0px;"><img src="' . $this->media['check'] . '" style="height: 14px; width: 14px; position: relative; top: 1px;" /> Some files were changed</td></tr>';
80
+ }
81
+
82
+ if ( ! empty( $files_deleted ) ) {
83
+ $body .= '<tr><td style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #404040;padding-bottom: 5px;padding-top: 0px;"><img src="' . $this->media['check'] . '" style="height: 14px; width: 14px; position: relative; top: 1px;" /> Some files were deleted</td></tr>';
84
+ }
85
+
86
+ $body .= '<tr><td style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #404040;padding-bottom: 20px;padding-top: 20px;">Click <a href="' . add_query_arg( 'page', 'wfcm-file-changes', admin_url( 'admin.php' ) ) . '" target="_blank" style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #149247;">here</a> to see the file changes.</td></tr>';
87
+
88
+ $body .= '</table></td></tr><!-- Table Border Start --><!-- Desc End --></table></td></tr><!-- Website File Changes End -->';
89
+ }
90
+
91
+ // No changes to report
92
+ if ( empty( $files_added ) && empty( $files_modified ) && empty( $files_deleted ) && class_exists( 'Website_File_Changes_Monitor' ) ) {
93
+ $body .= '<!-- Website File Changes Start --><tr><td><table width="100%" cellpadding="0" cellspacing="0" border="0"><!-- Title Start --><tr><td style="font-family: Verdana, sans-serif; font-weight: bold; font-size: 20px; line-height: 28px; color: #404040; text-align: left; padding-bottom: 13px;">Website File Changes</td></tr><!-- Title End --><!-- Desc Start --><!-- Table Border Start --><tr><td style="padding-bottom: 40px;"><table width="100%" cellpadding="0" cellspacing="0" border="0">';
94
+
95
+ $body .= '<tr><td style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #404040;padding-bottom: 5px;">Everything is looking good. No file changes detected during the last scan that ran on '. date( 'd/m/Y', get_option( 'wfcm_last-scan-timestamp' ) ) .' at '. date( 'H:i:s', get_option( 'wfcm_last-scan-timestamp' ) ) .'.</td></tr>';
96
+
97
+ $body .= '</table></td></tr><!-- Table Border Start --><!-- Desc End --></table></td></tr><!-- Website File Changes End -->';
98
+ }
99
+ }
100
+
101
+ // No WFCM plugin found.
102
+ if ( ! class_exists( 'Website_File_Changes_Monitor' ) ) {
103
+ $body .= '<!-- Website File Changes Start --><tr><td><table width="100%" cellpadding="0" cellspacing="0" border="0"><!-- Title Start --><tr><td style="font-family: Verdana, sans-serif; font-weight: bold; font-size: 20px; line-height: 28px; color: #404040; text-align: left; padding-bottom: 13px;">Website File Changes</td></tr><!-- Title End --><!-- Desc Start --><!-- Table Border Start --><tr><td style="padding-bottom: 40px;"><table width="100%" cellpadding="0" cellspacing="0" border="0">';
104
+
105
+ $body .= '<tr><td style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #404040;padding-bottom: 20px;padding-top: 0px;"><img src="' . $this->media['check'] . '" style="height: 14px; width: 14px; position: relative; top: 1px;" /> To be alerted of file changes install the <a href="https://www.wpwhitesecurity.com/wordpress-plugins/website-file-changes-monitor/" target="_blank" style="font-family: Verdana, sans-serif; font-weight: normal; font-size: 16px; line-height: 28px; color: #149247;">Website File Changes Monitor</a>, a plugin we developed to detect file changes. Once installed, the plugin fully integrates with WP Activity Log.</td></tr>';
106
+
107
+ $body .= '</table></td></tr><!-- Table Border Start --><!-- Desc End --></table></td></tr><!-- Website File Changes End -->';
108
+ }
109
+
110
+ return $body;
111
+ }
112
+
113
+ public function get_plugin_filename() {
114
+ return 'website-file-changes-monitor/website-file-changes-monitor.php';
115
+ }
116
+ }
117
+ }
classes/ThirdPartyExtensions/WPFormsExtension.php CHANGED
@@ -10,10 +10,10 @@ if ( ! class_exists( 'WSAL_WPFormsExtension' ) ) {
10
  'addon_for' => 'wpforms',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'wpforms.png',
13
- 'plugin_slug' => 'wp-security-audit-log-add-on-for-wpforms/wsal-wpforms.php',
14
  'plugin_basename' => 'wsal-wpforms.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/wp-security-audit-log-add-on-for-wpforms.latest-stable.zip',
16
- 'event_tab_id' => '#tab-wpforms',
17
  'plugin_description' => 'Keep a record of when someone adds, modifies or deletes forms, entries and more in the WPForms plugin.',
18
  )
19
  );
@@ -49,5 +49,9 @@ if ( ! class_exists( 'WSAL_WPFormsExtension' ) ) {
49
  public function get_color() {
50
  return '#e27730';
51
  }
 
 
 
 
52
  }
53
  }
10
  'addon_for' => 'wpforms',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'wpforms.png',
13
+ 'plugin_slug' => $this->get_plugin_filename(),
14
  'plugin_basename' => 'wsal-wpforms.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/wp-security-audit-log-add-on-for-wpforms.latest-stable.zip',
16
+ 'event_tab_id' => '#cat-wpforms',
17
  'plugin_description' => 'Keep a record of when someone adds, modifies or deletes forms, entries and more in the WPForms plugin.',
18
  )
19
  );
49
  public function get_color() {
50
  return '#e27730';
51
  }
52
+
53
+ public function get_plugin_filename() {
54
+ return 'wp-security-audit-log-add-on-for-wpforms/wsal-wpforms.php';
55
+ }
56
  }
57
  }
classes/ThirdPartyExtensions/WooCommerceExtension.php CHANGED
@@ -15,10 +15,10 @@ if ( ! class_exists( 'WSAL_WooCommerceExtension' ) ) {
15
  'addon_for' => 'woocommerce',
16
  'title' => $this->get_plugin_name(),
17
  'image_filename' => 'woocommerce.png',
18
- 'plugin_slug' => 'wp-activity-log-for-woocommerce/wsal-woocommerce.php',
19
  'plugin_basename' => 'wsal-woocommerce.php',
20
  'plugin_url' => 'https://downloads.wordpress.org/plugin/wp-activity-log-for-woocommerce.latest-stable.zip',
21
- 'event_tab_id' => '#tab-woocommerce',
22
  'plugin_description' => 'Keep a log of your team\'s store settings, products, orders, coupons and any other changes they might do on your eCommerce store.',
23
  ),
24
  );
@@ -88,5 +88,9 @@ if ( ! class_exists( 'WSAL_WooCommerceExtension' ) ) {
88
  public function get_color() {
89
  return '#7f54b3';
90
  }
 
 
 
 
91
  }
92
  }
15
  'addon_for' => 'woocommerce',
16
  'title' => $this->get_plugin_name(),
17
  'image_filename' => 'woocommerce.png',
18
+ 'plugin_slug' => $this->get_plugin_filename(),
19
  'plugin_basename' => 'wsal-woocommerce.php',
20
  'plugin_url' => 'https://downloads.wordpress.org/plugin/wp-activity-log-for-woocommerce.latest-stable.zip',
21
+ 'event_tab_id' => '#cat-woocommerce',
22
  'plugin_description' => 'Keep a log of your team\'s store settings, products, orders, coupons and any other changes they might do on your eCommerce store.',
23
  ),
24
  );
88
  public function get_color() {
89
  return '#7f54b3';
90
  }
91
+
92
+ public function get_plugin_filename() {
93
+ return 'wp-activity-log-for-woocommerce/wsal-woocommerce.php';
94
+ }
95
  }
96
  }
classes/ThirdPartyExtensions/YoastSeoExtension.php CHANGED
@@ -10,10 +10,10 @@ if ( ! class_exists( 'WSAL_YoastSeoExtension' ) ) {
10
  'addon_for' => 'wp-seo',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'yoast.png',
13
- 'plugin_slug' => 'activity-log-wp-seo/activity-log-wp-seo.php',
14
  'plugin_basename' => 'activity-log-wp-seo.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/activity-log-wp-seo.latest-stable.zip',
16
- 'event_tab_id' => '#tab-yoast-seo',
17
  'plugin_description' => 'Keep a log of all the changes that you and your team do in the Yoast SEO metabox, plugin settings & much more.',
18
  )
19
  );
@@ -54,5 +54,9 @@ if ( ! class_exists( 'WSAL_YoastSeoExtension' ) ) {
54
  public function get_color() {
55
  return '#a4286a';
56
  }
 
 
 
 
57
  }
58
  }
10
  'addon_for' => 'wp-seo',
11
  'title' => $this->get_plugin_name(),
12
  'image_filename' => 'yoast.png',
13
+ 'plugin_slug' => $this->get_plugin_filename(),
14
  'plugin_basename' => 'activity-log-wp-seo.php',
15
  'plugin_url' => 'https://downloads.wordpress.org/plugin/activity-log-wp-seo.latest-stable.zip',
16
+ 'event_tab_id' => '#cat-yoast-seo',
17
  'plugin_description' => 'Keep a log of all the changes that you and your team do in the Yoast SEO metabox, plugin settings & much more.',
18
  )
19
  );
54
  public function get_color() {
55
  return '#a4286a';
56
  }
57
+
58
+ public function get_plugin_filename() {
59
+ return 'activity-log-wp-seo/activity-log-wp-seo.php';
60
+ }
61
  }
62
  }
classes/Upgrade/MetadataMigration.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly.
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Background process for handling the migration of selected metadata from the meta table to the occurrences table. This
10
+ * was part of database schema changes introduced in version 4.4.0.
11
+ *
12
+ * It handles metadata migration for 1 connection defined as part of the process information. This can be either "local"
13
+ * to work with the local WP database or a name of connection defined by the Integrations extension.
14
+ *
15
+ * @package wsal
16
+ * @subpackage upgrade
17
+ * @since 4.4.0
18
+ */
19
+ class WSAL_Upgrade_MetadataMigration extends WSAL_Vendor\WP_Background_Process {
20
+
21
+ /**
22
+ * Name of the option holding the information about ongoing metadata migration.
23
+ *
24
+ * Note: the wsal_ prefix is automatically added by plugin's settings handling functions.
25
+ *
26
+ * @var string
27
+ */
28
+ const OPTION_NAME_MIGRATION_INFO = 'meta_data_migration_info_440';
29
+
30
+ /**
31
+ * @inheritDoc
32
+ */
33
+ protected $action = 'wsal_meta_data_migration_440';
34
+
35
+ /**
36
+ * Displays an admin notice if a metadata migration is in progress.
37
+ */
38
+ public static function maybe_display_progress_admin_notice() {
39
+ if ( ! is_user_logged_in() ) {
40
+ // Don't show to anonymous users (obviously).
41
+ return;
42
+ }
43
+
44
+ $current_user = get_userdata( get_current_user_id() );
45
+ if ( false === $current_user ) {
46
+ // Bail if there is a problem retrieving the current user.
47
+ return;
48
+ }
49
+
50
+ $is_admin = in_array( 'administrator', $current_user->roles, true ) || ( function_exists( 'is_super_admin' ) && is_super_admin( $current_user->ID ) );
51
+ if ( ! $is_admin ) {
52
+ // Don't show to admin users.
53
+ return;
54
+ }
55
+
56
+ $plugin = WpSecurityAuditLog::GetInstance();
57
+ $existing_info = $plugin->GetGlobalSetting( self::OPTION_NAME_MIGRATION_INFO, array() );
58
+ if ( empty( $existing_info ) ) {
59
+ return;
60
+ }
61
+ ?>
62
+ <div class="notice notice-info">
63
+ <div class="notice-content-wrapper">
64
+ <p>
65
+ <strong><?php esc_html_e( 'Activity log database update in progress.', 'wp-security-audit-log' ); ?></strong>
66
+ <br />
67
+ <?php esc_html_e( 'WP Activity Log is updating the database tables where it stores the activity log. This is needed to upgrade the activity log tables to the new database schema, so the logs can be stored and read more efficiently. The duration of this process varies, depending on the number of events in the activity log. This process runs in the background and won\'t affect your website. During the upgrade, you might notice some "null" values in the activity log. This is temporary until the process is complete.', 'wp-security-audit-log' ); ?>
68
+ </p>
69
+ </div>
70
+ </div>
71
+ <?php
72
+ }
73
+
74
+ /**
75
+ * @inheritDoc
76
+ *
77
+ * @param array{start_time: int, processed_events_count: int, batch_size: int, connection: string} $item
78
+ */
79
+ protected function task( $item ) {
80
+ // migrate metadata for the next batch of events
81
+ $items_migrated = $this->process_next_batch( $item['connection'], $item['batch_size'] );
82
+ if ( 0 === $items_migrated ) {
83
+ // all metadata has been migrated
84
+ try {
85
+ // delete the migration job info to indicate that the migration is done
86
+ self::remove_migration_info( $item['connection'] );
87
+
88
+ } catch ( Exception $exception ) {
89
+ $this->handle_error( $exception );
90
+ }
91
+
92
+ return false;
93
+ }
94
+
95
+ // update and save the migration info
96
+ $item['processed_events_count'] += $items_migrated;
97
+ self::store_migration_info( $item );
98
+
99
+ return $item;
100
+ }
101
+
102
+ /**
103
+ * @param string $connection
104
+ * @param int $batch_size
105
+ *
106
+ * @return int
107
+ */
108
+ private function process_next_batch( $connection, $batch_size ) {
109
+ $plugin = WpSecurityAuditLog::GetInstance();
110
+ if ( 'local' !== $connection && ! is_null( $plugin->external_db_util ) ) {
111
+ $connection = $plugin->external_db_util->get_connection( $connection );
112
+ if ( false === $connection ) {
113
+ return 0;
114
+ }
115
+ }
116
+
117
+ $connector = $plugin->getConnector( $connection );
118
+ /** @var WSAL_Adapters_MySQL_Occurrence $occurrence_adapter */
119
+ $occurrence_adapter = $connector->getAdapter( 'Occurrence' );
120
+
121
+ $occurrences_to_migrate = $occurrence_adapter->get_all_with_meta_to_migrate( $batch_size );
122
+ if ( ! empty( $occurrences_to_migrate ) ) {
123
+ $migrated_meta_keys = array_keys( WSAL_Models_Occurrence::$migrated_meta );
124
+ $lowercase_migrated_meta_keys = array_map( 'strtolower', $migrated_meta_keys );
125
+ foreach ( $occurrences_to_migrate as $occurrence ) {
126
+ $all_metadata = $occurrence_adapter->GetMultiMeta( $occurrence );
127
+ if ( ! empty( $all_metadata ) ) {
128
+ foreach ( $all_metadata as $meta_model ) {
129
+ $meta_key = $meta_model->name;
130
+ $lowercase_meta_key = strtolower($meta_key);
131
+
132
+ // We use lowercase meta keys to make sure we handle even legacy meta keys correctly, for
133
+ // example "username" was changed to "Username" at some point.
134
+ if ( in_array( $lowercase_meta_key , $lowercase_migrated_meta_keys ) ) {
135
+ // this will store the meta in the occ table if it belongs there
136
+ $is_empty_string = is_string( $meta_model->value ) && 0 === strlen( $meta_model->value );
137
+ if ( ! $is_empty_string && in_array( $meta_key, $migrated_meta_keys )) {
138
+ // The meta is set in the occurrence object on if it is an exact match, otherwise we
139
+ // would end up writing and deleting the same meta key endlessly.
140
+ $occurrence->SetMetaValue( $meta_key, $meta_model->value );
141
+ }
142
+
143
+ $meta_model->Delete();
144
+ }
145
+ }
146
+
147
+ $occurrence->Save();
148
+ }
149
+ }
150
+ }
151
+
152
+ return count( $occurrences_to_migrate );
153
+ }
154
+
155
+ /**
156
+ * Removes migration info for a particular connection.
157
+ *
158
+ * @param string $connection_name Connection name.
159
+ */
160
+ public static function remove_migration_info( $connection_name ) {
161
+ $plugin = WpSecurityAuditLog::GetInstance();
162
+ $existing_info = $plugin->GetGlobalSetting( self::OPTION_NAME_MIGRATION_INFO, [] );
163
+
164
+ if ( array_key_exists( $connection_name, $existing_info ) ) {
165
+ unset( $existing_info[ $connection_name ] );
166
+ }
167
+
168
+ if ( empty( $existing_info ) ) {
169
+ $plugin->DeleteGlobalSetting( self::OPTION_NAME_MIGRATION_INFO );
170
+ } else {
171
+ $plugin->SetGlobalSetting( self::OPTION_NAME_MIGRATION_INFO, $existing_info );
172
+ }
173
+ }
174
+
175
+ /**
176
+ * @param Exception $exception
177
+ */
178
+ private function handle_error( $exception ) {
179
+ // @todo handle migration error
180
+ }
181
+
182
+ /**
183
+ * Stores or updates migration info for one particular connection.
184
+ *
185
+ * @param array{start_time: int, processed_events_count: int, batch_size: int, connection: string} $info
186
+ */
187
+ public static function store_migration_info( $info ) {
188
+ $plugin = WpSecurityAuditLog::GetInstance();
189
+ $existing_info = $plugin->GetGlobalSetting( self::OPTION_NAME_MIGRATION_INFO, [] );
190
+ $connection_name = $info['connection'];
191
+
192
+ $existing_info[ $connection_name ] = $info;
193
+ $plugin->SetGlobalSetting( self::OPTION_NAME_MIGRATION_INFO, $existing_info );
194
+ }
195
+ }
classes/Upgrade/Upgrade_43000_to_44400.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class handles upgrade changes from version 43000 to 44400.
5
+ *
6
+ * @package wsal
7
+ * @subpackage upgrade
8
+ *
9
+ * @since 4.4.0
10
+ */
11
+ class WSAL_Upgrade_43000_to_44400 {
12
+
13
+ /**
14
+ * Plugin instance.
15
+ *
16
+ * @var WpSecurityAuditLog
17
+ */
18
+ private $plugin;
19
+
20
+ /**
21
+ * Constructor.
22
+ *
23
+ * @param WpSecurityAuditLog $plugin Plugin instance.
24
+ */
25
+ public function __construct( WpSecurityAuditLog $plugin ) {
26
+ $this->plugin = $plugin;
27
+ }
28
+
29
+ /**
30
+ * Runs the upgrade process.
31
+ */
32
+ public function run() {
33
+
34
+ // Remove some forgotten WFCM settings from the options table.
35
+ $this->remove_wfcm_leftover_settings();
36
+
37
+ // Change occurrence table in local database.
38
+ $this->upgrade_occurrence_table( 'local' );
39
+
40
+
41
+ $this->stop_autoloading_some_settings();
42
+ }
43
+
44
+ /**
45
+ * Removes a bunch of legacy WFCM extension related settings.
46
+ */
47
+ private function remove_wfcm_leftover_settings() {
48
+ // Remove all settings related to WFCM plugin.
49
+ $not_found_page_related_settings = array(
50
+ 'wsal_scan-in-progress',
51
+ 'wsal_last-scanned',
52
+ 'wsal_is_initial_scan_0',
53
+ 'wsal_is_initial_scan_1',
54
+ 'wsal_is_initial_scan_2',
55
+ 'wsal_is_initial_scan_3',
56
+ 'wsal_is_initial_scan_4',
57
+ 'wsal_is_initial_scan_5',
58
+ 'wsal_is_initial_scan_6',
59
+ 'wsal_last_scan_start',
60
+ 'wsal_scanned_dirs',
61
+ );
62
+ foreach ( $not_found_page_related_settings as $setting_name ) {
63
+ $this->plugin->DeleteGlobalSetting( $setting_name );
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Upgrades an occurrence table using given connection.
69
+ *
70
+ * It also kicks-off a metadata migration in the background.
71
+ *
72
+ * @param string|array $connection Connection alias or configuration data.
73
+ *
74
+ * @throws Freemius_Exception Freemius exception.
75
+ */
76
+ private function upgrade_occurrence_table( $connection ) {
77
+ $connector = $this->plugin->getConnector( $connection );
78
+ /** @var WSAL_Adapters_MySQL_Occurrence $occurrence_adapter */
79
+ $occurrence_adapter = $connector->getAdapter( 'Occurrence' );
80
+
81
+ // Skip the upgrade it the table does not exist for some reason.
82
+ if ( ! $connector->isInstalled() ) {
83
+ return;
84
+ }
85
+
86
+ $table_name = $occurrence_adapter->GetTable();
87
+ $connector->query( $this->get_occurrence_table_upgrade_query( $table_name ) );
88
+
89
+ // Check if there are any events to process.
90
+ if ( $occurrence_adapter->Count() > 0 ) {
91
+ // Create a background job to migrate the metadata.
92
+ $job_info = array(
93
+ 'start_time' => current_time( 'timestamp' ), // phpcs:ignore
94
+ 'processed_events_count' => 0,
95
+ 'batch_size' => 50,
96
+ 'connection' => is_array( $connection ) ? $connection['name'] : $connection,
97
+ );
98
+
99
+ // Store the initial info to the db.
100
+ WSAL_Upgrade_MetadataMigration::store_migration_info( $job_info );
101
+
102
+ // Create and dispatch the background process itself.
103
+ $bg_process = new WSAL_Upgrade_MetadataMigration();
104
+ $bg_process->push_to_queue( $job_info );
105
+ $bg_process->save();
106
+ $bg_process->dispatch();
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Builds an upgrade query for the occurrence table.
112
+ *
113
+ * @param string $table_name Table name.
114
+ *
115
+ * @return string
116
+ */
117
+ private function get_occurrence_table_upgrade_query( $table_name ) {
118
+ return "ALTER TABLE {$table_name}"
119
+ . ' DROP COLUMN is_read, '
120
+ . ' DROP COLUMN is_migrated, '
121
+ . " ADD client_ip VARCHAR(255) NOT NULL DEFAULT '',"
122
+ . ' ADD severity BIGINT NOT NULL DEFAULT 0,'
123
+ . " ADD object VARCHAR(255) NOT NULL DEFAULT '',"
124
+ . " ADD event_type VARCHAR(255) NOT NULL DEFAULT '',"
125
+ . " ADD user_agent VARCHAR(255) NOT NULL DEFAULT '',"
126
+ . " ADD user_roles VARCHAR(255) NOT NULL DEFAULT '',"
127
+ . ' ADD username VARCHAR(255) NULL,'
128
+ . ' ADD user_id BIGINT NULL ,'
129
+ . " ADD session_id VARCHAR(255) NOT NULL DEFAULT '',"
130
+ . " ADD post_status VARCHAR(255) NOT NULL DEFAULT '',"
131
+ . " ADD post_type VARCHAR(255) NOT NULL DEFAULT '',"
132
+ . ' ADD post_id BIGINT NOT NULL DEFAULT 0;';
133
+ }
134
+
135
+
136
+ /**
137
+ * Change all but selected plugin settings to stop autoloading.
138
+ */
139
+ private function stop_autoloading_some_settings() {
140
+ $settings_to_leave_on_autoload = array(
141
+ 'wsal_adapter-connection',
142
+ 'wsal_admin-bar-notif-updates',
143
+ 'wsal_db_version',
144
+ 'wsal_disable-admin-bar-notif',
145
+ 'wsal_frontend-events',
146
+ 'wsal_plugin-viewers',
147
+ 'wsal_restrict-log-viewer',
148
+ 'wsal_restrict-plugin-settings',
149
+ 'wsal_setup-modal-dismissed',
150
+ 'wsal_version',
151
+ 'wsal_freemius_state',
152
+ 'wsal_only-me-user-id',
153
+ );
154
+
155
+ // phpcs:disable
156
+ global $wpdb;
157
+ $plugin_options = $wpdb->get_results(
158
+ 'SELECT option_name, option_value '
159
+ . " FROM $wpdb->options "
160
+ . " WHERE option_name LIKE '" . WpSecurityAuditLog::OPTIONS_PREFIX . "%';",
161
+ ARRAY_A
162
+ );
163
+ // phpcs:enable
164
+
165
+ if ( ! empty( $plugin_options ) ) {
166
+ foreach ( $plugin_options as $option ) {
167
+ if ( ! in_array( $option['option_name'], $settings_to_leave_on_autoload ) ) { // phpcs:ignore
168
+ $value = maybe_unserialize( $option['option_value'] );
169
+ $this->plugin->SetGlobalSetting( $option['option_name'], $value, false );
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
classes/Utilities/FileSystemUtils.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Utility class for handling certain file system related functionality.
5
+ *
6
+ * @package wsal
7
+ * @since 4.4.0
8
+ */
9
+ class WSAL_Utilities_FileSystemUtils {
10
+ /**
11
+ * Returns a list of files matching given pattern in a given directory.
12
+ *
13
+ * It uses transients to cache the list of files for a day.
14
+ *
15
+ * @param string $directory Directory to search.
16
+ * @param string $pattern Filename pattern to narrow down the list of files.
17
+ *
18
+ * @return string
19
+ *
20
+ * @since 4.4.0
21
+ */
22
+ public static function read_files_in_folder( $directory, $pattern ) {
23
+ $folder_slashed = trailingslashit( $directory );
24
+ $cache_key = WpSecurityAuditLog::OPTIONS_PREFIX . '_file_list_' . md5( $folder_slashed . $pattern );
25
+ $cached_data = get_transient( $cache_key );
26
+ if ( is_array( $cached_data ) ) {
27
+ return $cached_data;
28
+ }
29
+
30
+ $result = array();
31
+ $handle = opendir( $directory );
32
+ if ( $handle ) {
33
+ $ignore_list = array( '.', '..' );
34
+ $regexp = '/' . str_replace( array( '.', '*' ), array( '\.', '.*' ), $pattern ) . '/';
35
+ while ( false !== ( $file_name = readdir( $handle ) ) ) {
36
+ if ( ! in_array( $file_name, $ignore_list, true ) && preg_match( $regexp, $file_name ) ) {
37
+ array_push( $result, $folder_slashed . $file_name );
38
+ }
39
+ }
40
+ closedir( $handle );
41
+ }
42
+
43
+ set_transient( $cache_key, $result, DAY_IN_SECONDS );
44
+
45
+ return $result;
46
+ }
47
+ }
classes/Utilities/PluginInstallAndActivate.php CHANGED
@@ -133,17 +133,18 @@ if ( ! class_exists( 'WSAL_PluginInstallAndActivate' ) ) {
133
  public static function get_installable_plugins() {
134
  $plugins = array(
135
  array(
136
- 'addon_for' => 'wfcm',
137
- 'title' => 'Website File Changes Monitor',
138
- 'plugin_slug' => 'website-file-changes-monitor/website-file-changes-monitor.php',
139
- 'plugin_url' => 'https://downloads.wordpress.org/plugin/website-file-changes-monitor.latest-stable.zip',
140
  ),
141
  );
142
- // runs through a filter so it can be added to programatically.
 
143
  // NOTE: this means when using we need to test it's still an array.
144
  $installable_plugins = apply_filters( 'wsal_filter_installable_plugins', $plugins );
145
 
146
- // Sort them into a a nice order.
147
  array_multisort( array_column( $installable_plugins, 'title' ), SORT_ASC, $installable_plugins );
148
 
149
  return $installable_plugins;
133
  public static function get_installable_plugins() {
134
  $plugins = array(
135
  array(
136
+ 'addon_for' => 'wfcm',
137
+ 'title' => 'Website File Changes Monitor',
138
+ 'plugin_slug' => 'website-file-changes-monitor/website-file-changes-monitor.php',
139
+ 'plugin_url' => 'https://downloads.wordpress.org/plugin/website-file-changes-monitor.latest-stable.zip',
140
  ),
141
  );
142
+
143
+ // runs through a filter, so it can be added to programmatically.
144
  // NOTE: this means when using we need to test it's still an array.
145
  $installable_plugins = apply_filters( 'wsal_filter_installable_plugins', $plugins );
146
 
147
+ // Sort them into a nice order.
148
  array_multisort( array_column( $installable_plugins, 'title' ), SORT_ASC, $installable_plugins );
149
 
150
  return $installable_plugins;
classes/Utilities/PluginInstallerAction.php CHANGED
@@ -11,7 +11,7 @@
11
  if ( ! class_exists( 'WSAL_PluginInstallerAction' ) ) {
12
 
13
  /**
14
- * Class to handle the installtion and activation of plugins.
15
  *
16
  * @since 4.0.1
17
  */
@@ -63,14 +63,20 @@ if ( ! class_exists( 'WSAL_PluginInstallerAction' ) ) {
63
  }
64
  }
65
 
66
- // validate that the plugin is in the allowed list.
67
  $valid = false;
68
- foreach ( $predefined_plugins as $plugin ) {
69
- // if we have a valid plugin then break.
70
- if ( $valid ) {
71
- break;
 
 
 
 
 
 
 
72
  }
73
- $valid = ( $plugin_zip === $plugin['plugin_url'] && $plugin_slug === $plugin['plugin_slug'] ) ? true : false;
74
  }
75
 
76
  // bail early if we didn't get a valid url and slug to install.
@@ -100,6 +106,11 @@ if ( ! class_exists( 'WSAL_PluginInstallerAction' ) ) {
100
  $result = 'success';
101
  }
102
 
 
 
 
 
 
103
  wp_send_json( $result );
104
  }
105
 
11
  if ( ! class_exists( 'WSAL_PluginInstallerAction' ) ) {
12
 
13
  /**
14
+ * Class to handle the installation and activation of plugins.
15
  *
16
  * @since 4.0.1
17
  */
63
  }
64
  }
65
 
66
+ // validate that the plugin is in the allowed list, or it is our helper plugin with external libraries.
67
  $valid = false;
68
+ $helper_plugin_installation = 'wsal-external-libraries/wsal-external-libraries.php' === $plugin_slug;
69
+ if ( $helper_plugin_installation ) {
70
+ $valid = true;
71
+ } else {
72
+ foreach ( $predefined_plugins as $plugin ) {
73
+ // if we have a valid plugin then break.
74
+ if ( $valid ) {
75
+ break;
76
+ }
77
+
78
+ $valid = $plugin_zip === $plugin['plugin_url'] && $plugin_slug === $plugin['plugin_slug'];
79
  }
 
80
  }
81
 
82
  // bail early if we didn't get a valid url and slug to install.
106
  $result = 'success';
107
  }
108
 
109
+ // if we're installing our helper plugin, we also need to delete the nudge to install the helper plugin
110
+ if ( $helper_plugin_installation ) {
111
+ WpSecurityAuditLog::GetInstance()->DeleteGlobalSetting( 'show-helper-plugin-needed-nudge' );
112
+ }
113
+
114
  wp_send_json( $result );
115
  }
116
 
classes/Utilities/RequestUtils.php CHANGED
@@ -46,4 +46,15 @@ class WSAL_Utilities_RequestUtils {
46
 
47
  return true;
48
  }
 
 
 
 
 
 
 
 
 
 
 
49
  }
46
 
47
  return true;
48
  }
49
+
50
+ /**
51
+ * Check if the float is IPv4 instead.
52
+ *
53
+ * @param float $ip_address - Number to check.
54
+ *
55
+ * @return bool result validation
56
+ */
57
+ public static function is_ip_address( $ip_address ) {
58
+ return filter_var( $ip_address, FILTER_VALIDATE_IP ) !== false;
59
+ }
60
  }
classes/Utilities/UserUtils.php CHANGED
@@ -23,8 +23,10 @@ class WSAL_Utilities_UsersUtils {
23
  private static $user_label_setting;
24
 
25
  /**
 
 
26
  * @param WpSecurityAuditLog $plugin Instance of WpSecurityAuditLog.
27
- * @param WP_User $user WordPress user object.
28
  *
29
  * @return string
30
  * @since 4.3.0
@@ -39,10 +41,15 @@ class WSAL_Utilities_UsersUtils {
39
  }
40
 
41
  if ( 'first_last_name' === self::$user_label_setting && ( ! empty( $user->first_name ) || ! empty( $user->last_name ) ) ) {
42
- return trim( implode( ' ', [
43
- $user->first_name,
44
- $user->last_name
45
- ] ) );
 
 
 
 
 
46
  }
47
 
48
  return $user->user_login;
@@ -88,12 +95,26 @@ class WSAL_Utilities_UsersUtils {
88
  return '';
89
  }
90
 
91
- $tooltip = '<strong>' . esc_attr__( 'Username: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->user_login . '</br>';
92
  $tooltip .= ( ! empty( $user->data->first_name ) ) ? '<strong>' . esc_attr__( 'First name: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->first_name . '</br>' : '';
93
  $tooltip .= ( ! empty( $user->data->first_name ) ) ? '<strong>' . esc_attr__( 'Last Name: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->first_name . '</br>' : '';
94
  $tooltip .= '<strong>' . esc_attr__( 'Email: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->user_email . '</br>';
95
  $tooltip .= '<strong>' . esc_attr__( 'Nickname: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->user_nicename . '</br></br>';
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  return $tooltip;
98
  }
99
 
@@ -149,4 +170,4 @@ class WSAL_Utilities_UsersUtils {
149
  return '<i>' . esc_html__( 'Unknown', 'wp-security-audit-log' ) . '</i>';
150
 
151
  }
152
- }
23
  private static $user_label_setting;
24
 
25
  /**
26
+ * Build the correct label to display for a given user.
27
+ *
28
  * @param WpSecurityAuditLog $plugin Instance of WpSecurityAuditLog.
29
+ * @param WP_User $user WordPress user object.
30
  *
31
  * @return string
32
  * @since 4.3.0
41
  }
42
 
43
  if ( 'first_last_name' === self::$user_label_setting && ( ! empty( $user->first_name ) || ! empty( $user->last_name ) ) ) {
44
+ return trim(
45
+ implode(
46
+ ' ',
47
+ array(
48
+ $user->first_name,
49
+ $user->last_name,
50
+ )
51
+ )
52
+ );
53
  }
54
 
55
  return $user->user_login;
95
  return '';
96
  }
97
 
98
+ $tooltip = '<strong>' . esc_attr__( 'Username: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->user_login . '</br>';
99
  $tooltip .= ( ! empty( $user->data->first_name ) ) ? '<strong>' . esc_attr__( 'First name: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->first_name . '</br>' : '';
100
  $tooltip .= ( ! empty( $user->data->first_name ) ) ? '<strong>' . esc_attr__( 'Last Name: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->first_name . '</br>' : '';
101
  $tooltip .= '<strong>' . esc_attr__( 'Email: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->user_email . '</br>';
102
  $tooltip .= '<strong>' . esc_attr__( 'Nickname: ', 'wp-security-audit-log' ) . '</strong>' . $user->data->user_nicename . '</br></br>';
103
 
104
+ /**
105
+ * WSAL Filter: `wsal_additional_user_tooltip_content'
106
+ *
107
+ * Allows 3rd parties to append HTML to the user tooltip content in audit log viewer.
108
+ *
109
+ * @since 4.4.0
110
+ *
111
+ * @param string $content Blank string to append to.
112
+ * @param object $user - User object.
113
+ */
114
+ $additional_content = apply_filters( 'wsal_additional_user_tooltip_content', '', $user );
115
+
116
+ $tooltip .= $additional_content;
117
+
118
  return $tooltip;
119
  }
120
 
170
  return '<i>' . esc_html__( 'Unknown', 'wp-security-audit-log' ) . '</i>';
171
 
172
  }
173
+ }
classes/ViewManager.php CHANGED
@@ -89,7 +89,7 @@ class WSAL_ViewManager {
89
  $skip_views = apply_filters( 'wsal_skip_views', $skip_views );
90
 
91
  // Load views.
92
- foreach ( glob( $this->_plugin->GetBaseDir() . 'classes/Views/*.php' ) as $file ) {
93
  if ( empty( $skip_views ) || ! in_array( $file, $skip_views ) ) {
94
  $this->AddFromFile( $file );
95
  }
@@ -118,9 +118,6 @@ class WSAL_ViewManager {
118
  new WSAL_Views_SetupWizard( $plugin );
119
  }
120
 
121
- // Reorder WSAL submenu.
122
- add_filter( 'custom_menu_order', array( $this, 'reorder_wsal_submenu' ), 10, 1 );
123
-
124
 
125
  add_action( 'admin_head', array( $this, 'hide_freemius_sites_section' ) );
126
 
@@ -131,7 +128,6 @@ class WSAL_ViewManager {
131
  }
132
  }
133
 
134
-
135
  /**
136
  * Display notice if user is using older version of WFCM
137
  */
@@ -453,50 +449,6 @@ class WSAL_ViewManager {
453
  return $not_show;
454
  }
455
 
456
- /**
457
- * Reorder WSAL Submenu.
458
- *
459
- * @since 3.2.4
460
- *
461
- * @param boolean $menu_order - Custom order.
462
- * @return boolean
463
- */
464
- public function reorder_wsal_submenu( $menu_order ) {
465
- // Get global $submenu order.
466
- global $submenu;
467
-
468
- // Get WSAL admin menu.
469
- $auditlog_menu = isset( $submenu['wsal-auditlog'] ) ? $submenu['wsal-auditlog'] : false;
470
- $help_link = new stdClass();
471
- $account_link = new stdClass();
472
-
473
- if ( ! empty( $auditlog_menu ) ) {
474
- foreach ( $auditlog_menu as $key => $auditlog_submenu ) {
475
- if ( 'wsal-help' === $auditlog_submenu[2] ) {
476
- $help_link->key = $key;
477
- $help_link->menu = $auditlog_submenu;
478
- } elseif ( 'wsal-auditlog-account' === $auditlog_submenu[2] ) {
479
- $account_link->key = $key;
480
- $account_link->menu = $auditlog_submenu;
481
- }
482
- }
483
- }
484
-
485
- if ( ! empty( $help_link ) && ! empty( $account_link ) ) {
486
- // Swap the menus at their positions.
487
- if ( isset( $help_link->key ) && isset( $account_link->menu ) ) {
488
- $submenu['wsal-auditlog'][ $help_link->key ] = $account_link->menu;
489
- }
490
- if ( isset( $account_link->key ) && isset( $help_link->menu ) ) {
491
- $submenu['wsal-auditlog'][ $account_link->key ] = $help_link->menu;
492
- }
493
- if ( isset( $submenu['wsal-auditlog'] ) && is_array( $submenu['wsal-auditlog'] ) ) {
494
- ksort( $submenu['wsal-auditlog'] );
495
- }
496
- }
497
- return $menu_order;
498
- }
499
-
500
 
501
  /**
502
  * Hide Freemius sites section on the account page
89
  $skip_views = apply_filters( 'wsal_skip_views', $skip_views );
90
 
91
  // Load views.
92
+ foreach ( WSAL_Utilities_FileSystemUtils::read_files_in_folder( $this->_plugin->GetBaseDir() . 'classes/Views', '*.php' ) as $file ) {
93
  if ( empty( $skip_views ) || ! in_array( $file, $skip_views ) ) {
94
  $this->AddFromFile( $file );
95
  }
118
  new WSAL_Views_SetupWizard( $plugin );
119
  }
120
 
 
 
 
121
 
122
  add_action( 'admin_head', array( $this, 'hide_freemius_sites_section' ) );
123
 
128
  }
129
  }
130
 
 
131
  /**
132
  * Display notice if user is using older version of WFCM
133
  */
449
  return $not_show;
450
  }
451
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
  /**
454
  * Hide Freemius sites section on the account page
classes/Views/AuditLog.php CHANGED
@@ -79,6 +79,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
79
  add_action( 'wp_ajax_wsal_dismiss_advert', array( $this, 'wsal_dismiss_advert' ) );
80
  add_action( 'wp_ajax_wsal_dismiss_notice_addon_available', array( $this, 'dismiss_notice_addon_available' ) );
81
  add_action( 'wp_ajax_wsal_dismiss_missing_aws_sdk_nudge', array( $this, 'dismiss_missing_aws_sdk_nudge' ) );
 
82
  add_action( 'wp_ajax_wsal_dismiss_wp_pointer', array( $this, 'dismiss_wp_pointer' ) );
83
  add_action( 'all_admin_notices', array( $this, 'AdminNotices' ) );
84
  add_action( 'admin_enqueue_scripts', array( $this, 'load_pointers' ), 1000 );
@@ -172,7 +173,6 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
172
  'https://wpactivitylog.com/trial-premium-edition-plugin/'
173
  );
174
 
175
- // Buy Now button link.
176
  $buy_now = add_query_arg(
177
  array(
178
  'utm_source' => 'plugin',
@@ -227,6 +227,10 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
227
  $screen = get_current_screen();
228
 
229
 
 
 
 
 
230
  if ( $is_current_view && in_array( $screen->base, array( 'toplevel_page_wsal-auditlog', 'toplevel_page_wsal-auditlog-network' ) ) ) {
231
  // Grab list of installed plugins.
232
  $all_plugins = get_plugins();
@@ -792,7 +796,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
792
  }
793
 
794
  // Update freemius state.
795
- $this->_plugin->SetGlobalSetting( 'freemius_state', 'in' );
796
  } elseif ( 'no' === $choice ) {
797
  if ( ! is_multisite() ) {
798
  wsal_freemius()->skip_connection(); // Opt out.
@@ -801,7 +805,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
801
  }
802
 
803
  // Update freemius state.
804
- $this->_plugin->SetGlobalSetting( 'freemius_state', 'skipped' );
805
  }
806
 
807
  echo wp_json_encode(
@@ -1166,7 +1170,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
1166
  die();
1167
  }
1168
 
1169
- $this->_plugin->SetGlobalBooleanSetting( 'setup-modal-dismissed', true );
1170
  wp_send_json_success();
1171
  }
1172
 
@@ -1190,23 +1194,32 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
1190
  */
1191
  public function maybe_build_teaser_html( $event_meta ) {
1192
  $result = '';
1193
- if ( array_key_exists( 'PostType', $event_meta ) ) {
1194
- $extension = WSAL_AbstractExtension::get_extension_for_post_type( $event_meta['PostType'] );
1195
- if ( ! is_null( $extension ) ) {
1196
- $result .= '<div class="extension-ad" style="border-color: transparent transparent ' . $extension->get_color() . ' transparent;">';
1197
- $result .= '</div>';
1198
- $plugin_name = $extension->get_plugin_name();
1199
- $link_title = sprintf(
1200
- esc_html__( 'Install the activity log extension for %1$s for more detailed logging of changes done in %2$s.', 'wp-security-audit-log' ),
1201
- $plugin_name,
1202
- $plugin_name
1203
- );
1204
- $result .= '<a class="icon" title="' . $link_title . '" href="' . $this->get_third_party_plugins_tab_url() . '">';
1205
- $result .= '<img src="' . $extension->get_plugin_icon_url() . '" />';
1206
- $result .= '</div>';
1207
- }
1208
  }
1209
 
 
 
 
 
 
 
 
 
 
 
 
 
1210
  return $result;
1211
  }
1212
  }
79
  add_action( 'wp_ajax_wsal_dismiss_advert', array( $this, 'wsal_dismiss_advert' ) );
80
  add_action( 'wp_ajax_wsal_dismiss_notice_addon_available', array( $this, 'dismiss_notice_addon_available' ) );
81
  add_action( 'wp_ajax_wsal_dismiss_missing_aws_sdk_nudge', array( $this, 'dismiss_missing_aws_sdk_nudge' ) );
82
+ add_action( 'wp_ajax_wsal_dismiss_helper_plugin_needed_nudge', array( $this, 'dismiss_helper_plugin_needed_nudge' ) );
83
  add_action( 'wp_ajax_wsal_dismiss_wp_pointer', array( $this, 'dismiss_wp_pointer' ) );
84
  add_action( 'all_admin_notices', array( $this, 'AdminNotices' ) );
85
  add_action( 'admin_enqueue_scripts', array( $this, 'load_pointers' ), 1000 );
173
  'https://wpactivitylog.com/trial-premium-edition-plugin/'
174
  );
175
 
 
176
  $buy_now = add_query_arg(
177
  array(
178
  'utm_source' => 'plugin',
227
  $screen = get_current_screen();
228
 
229
 
230
+ if (class_exists('WSAL_Upgrade_MetadataMigration')) {
231
+ WSAL_Upgrade_MetadataMigration::maybe_display_progress_admin_notice();
232
+ }
233
+
234
  if ( $is_current_view && in_array( $screen->base, array( 'toplevel_page_wsal-auditlog', 'toplevel_page_wsal-auditlog-network' ) ) ) {
235
  // Grab list of installed plugins.
236
  $all_plugins = get_plugins();
796
  }
797
 
798
  // Update freemius state.
799
+ $this->_plugin->SetGlobalSetting( 'freemius_state', 'in', true );
800
  } elseif ( 'no' === $choice ) {
801
  if ( ! is_multisite() ) {
802
  wsal_freemius()->skip_connection(); // Opt out.
805
  }
806
 
807
  // Update freemius state.
808
+ $this->_plugin->SetGlobalSetting( 'freemius_state', 'skipped', true );
809
  }
810
 
811
  echo wp_json_encode(
1170
  die();
1171
  }
1172
 
1173
+ $this->_plugin->SetGlobalBooleanSetting( 'setup-modal-dismissed', true, true );
1174
  wp_send_json_success();
1175
  }
1176
 
1194
  */
1195
  public function maybe_build_teaser_html( $event_meta ) {
1196
  $result = '';
1197
+ if ( ! array_key_exists( 'PostType', $event_meta ) ) {
1198
+ return $result;
1199
+ }
1200
+
1201
+ $extension = WSAL_AbstractExtension::get_extension_for_post_type( $event_meta['PostType'] );
1202
+ if ( is_null( $extension ) ) {
1203
+ return $result;
1204
+ }
1205
+
1206
+ $plugin_filename = $extension->get_plugin_filename();
1207
+ if ( WSAL_PluginInstallAndActivate::is_plugin_installed( $plugin_filename ) && WpSecurityAuditLog::is_plugin_active( $plugin_filename ) ) {
1208
+ return $result;
 
 
 
1209
  }
1210
 
1211
+ $result .= '<div class="extension-ad" style="border-color: transparent transparent ' . $extension->get_color() . ' transparent;">';
1212
+ $result .= '</div>';
1213
+ $plugin_name = $extension->get_plugin_name();
1214
+ $link_title = sprintf(
1215
+ esc_html__( 'Install the activity log extension for %1$s for more detailed logging of changes done in %2$s.', 'wp-security-audit-log' ),
1216
+ $plugin_name,
1217
+ $plugin_name
1218
+ );
1219
+ $result .= '<a class="icon" title="' . $link_title . '" href="' . $this->get_third_party_plugins_tab_url() . '">';
1220
+ $result .= '<img src="' . $extension->get_plugin_icon_url() . '" />';
1221
+ $result .= '</div>';
1222
+
1223
  return $result;
1224
  }
1225
  }
classes/Views/EmailNotifications.php CHANGED
@@ -32,14 +32,14 @@ class WSAL_Views_EmailNotifications extends WSAL_ExtensionPlaceholderView {
32
  * Get View Name.
33
  */
34
  public function GetName() {
35
- return __( 'Notifications &#8682;', 'wp-security-audit-log' );
36
  }
37
 
38
  /**
39
  * Get View Weight.
40
  */
41
  public function GetWeight() {
42
- return 8;
43
  }
44
 
45
  /**
32
  * Get View Name.
33
  */
34
  public function GetName() {
35
+ return __( 'Email Notifications &#8682;', 'wp-security-audit-log' );
36
  }
37
 
38
  /**
39
  * Get View Weight.
40
  */
41
  public function GetWeight() {
42
+ return 2;
43
  }
44
 
45
  /**
classes/Views/Help.php CHANGED
@@ -131,7 +131,7 @@ class WSAL_Views_Help extends WSAL_AbstractView {
131
  * Method: Get View Weight.
132
  */
133
  public function GetWeight() {
134
- return 13;
135
  }
136
 
137
  /**
@@ -277,10 +277,22 @@ class WSAL_Views_Help extends WSAL_AbstractView {
277
  */
278
  private function sidebar() {
279
  $plugins_data = array(
 
 
 
 
 
 
 
 
 
 
 
 
280
  array(
281
  'img' => trailingslashit( WSAL_BASE_URL ) . 'img/help/password-policy-manager.jpg',
282
  'desc' => __( 'Enforce strong password policies on WordPress', 'wp-security-audit-log' ),
283
- 'alt' => 'Password Policy Manager',
284
  'link' => 'https://www.wpwhitesecurity.com/wordpress-plugins/password-policy-manager-wordpress/?utm_source=plugin&utm_medium=referral&utm_campaign=PPMWP&utm_content=WSAL+banner',
285
  ),
286
  array(
@@ -289,12 +301,6 @@ class WSAL_Views_Help extends WSAL_AbstractView {
289
  'alt' => 'Website File Changes Monitor',
290
  'link' => 'https://www.wpwhitesecurity.com/wordpress-plugins/website-file-changes-monitor/?utm_source=plugin&utm_medium=referral&utm_campaign=WFCM&utm_content=WSAL+banner',
291
  ),
292
- array(
293
- 'img' => trailingslashit( WSAL_BASE_URL ) . 'img/help/wp-2fa-img.jpg',
294
- 'desc' => __( 'Add an extra layer of security to your login pages with 2FA & require your users to use it.', 'wp-security-audit-log' ),
295
- 'alt' => 'WP 2FA',
296
- 'link' => 'https://www.wpwhitesecurity.com/wordpress-plugins/wp-2fa/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA&utm_content=WSAL+banner',
297
- ),
298
  array(
299
  'img' => trailingslashit( WSAL_BASE_URL ) . 'img/help/activity-log-for-mainwp.jpg',
300
  'desc' => __( 'See the child sites activity logs from the central MainWP dashboard', 'wp-security-audit-log' ),
131
  * Method: Get View Weight.
132
  */
133
  public function GetWeight() {
134
+ return 10;
135
  }
136
 
137
  /**
277
  */
278
  private function sidebar() {
279
  $plugins_data = array(
280
+ array(
281
+ 'img' => trailingslashit( WSAL_BASE_URL ) . 'img/help/wp-2fa-img.jpg',
282
+ 'desc' => __( 'Add an extra layer of security to your login pages with 2FA & require your users to use it.', 'wp-security-audit-log' ),
283
+ 'alt' => 'WP 2FA',
284
+ 'link' => 'https://wp2fa.io/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA&utm_content=WSAL+banner',
285
+ ),
286
+ array(
287
+ 'img' => trailingslashit( WSAL_BASE_URL ) . 'img/help/c4wp.jpg',
288
+ 'desc' => __( 'Protect website forms & login pages from spambots & automated attacks.', 'wp-security-audit-log' ),
289
+ 'alt' => 'Captcha 4WP',
290
+ 'link' => 'https://www.wpwhitesecurity.com/wordpress-plugins/captcha-plugin-wordpress/?utm_source=plugin&utm_medium=referral&utm_campaign=WP2FA&utm_content=WSAL+banner',
291
+ ),
292
  array(
293
  'img' => trailingslashit( WSAL_BASE_URL ) . 'img/help/password-policy-manager.jpg',
294
  'desc' => __( 'Enforce strong password policies on WordPress', 'wp-security-audit-log' ),
295
+ 'alt' => 'WPassword',
296
  'link' => 'https://www.wpwhitesecurity.com/wordpress-plugins/password-policy-manager-wordpress/?utm_source=plugin&utm_medium=referral&utm_campaign=PPMWP&utm_content=WSAL+banner',
297
  ),
298
  array(
301
  'alt' => 'Website File Changes Monitor',
302
  'link' => 'https://www.wpwhitesecurity.com/wordpress-plugins/website-file-changes-monitor/?utm_source=plugin&utm_medium=referral&utm_campaign=WFCM&utm_content=WSAL+banner',
303
  ),
 
 
 
 
 
 
304
  array(
305
  'img' => trailingslashit( WSAL_BASE_URL ) . 'img/help/activity-log-for-mainwp.jpg',
306
  'desc' => __( 'See the child sites activity logs from the central MainWP dashboard', 'wp-security-audit-log' ),
classes/Views/Reports.php CHANGED
@@ -32,14 +32,14 @@ class WSAL_Views_Reports extends WSAL_ExtensionPlaceholderView {
32
  * Method: Get View Name.
33
  */
34
  public function GetName() {
35
- return __( 'Reports &#8682;', 'wp-security-audit-log' );
36
  }
37
 
38
  /**
39
  * Method: Get View Weight.
40
  */
41
  public function GetWeight() {
42
- return 9;
43
  }
44
 
45
  /**
32
  * Method: Get View Name.
33
  */
34
  public function GetName() {
35
+ return __( 'Create Reports &#8682;', 'wp-security-audit-log' );
36
  }
37
 
38
  /**
39
  * Method: Get View Weight.
40
  */
41
  public function GetWeight() {
42
+ return 3;
43
  }
44
 
45
  /**
classes/Views/Search.php CHANGED
@@ -32,14 +32,14 @@ class WSAL_Views_Search extends WSAL_ExtensionPlaceholderView {
32
  * Method: Get View Name.
33
  */
34
  public function GetName() {
35
- return __( 'Search &#8682;', 'wp-security-audit-log' );
36
  }
37
 
38
  /**
39
  * Method: Get View Weight.
40
  */
41
  public function GetWeight() {
42
- return 11;
43
  }
44
 
45
  /**
32
  * Method: Get View Name.
33
  */
34
  public function GetName() {
35
+ return __( 'Log Search &#8682;', 'wp-security-audit-log' );
36
  }
37
 
38
  /**
39
  * Method: Get View Weight.
40
  */
41
  public function GetWeight() {
42
+ return 5;
43
  }
44
 
45
  /**
classes/Views/Settings.php CHANGED
@@ -183,7 +183,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
183
  * Method: Get View Weight.
184
  */
185
  public function GetWeight() {
186
- return 12;
187
  }
188
 
189
  /**
@@ -406,7 +406,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
406
  elementRef.checked = false;
407
  // Ensure the "no" option is reselected.
408
  jQuery('#delete_data_no').click();
409
- }
410
  }
411
  }
412
 
@@ -1138,7 +1138,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1138
  private function tab_audit_log_save() {
1139
  // Get $_POST global array.
1140
  $post_array = filter_input_array( INPUT_POST );
1141
-
1142
  // Get pruning date.
1143
  $pruning_date = isset( $post_array['PruningDate'] ) ? (int) sanitize_text_field( $post_array['PruningDate'] ) : false;
1144
  $pruning_unit = isset( $post_array['pruning-unit'] ) ? sanitize_text_field( $post_array['pruning-unit'] ) : false;
@@ -1412,19 +1412,22 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1412
  </td>
1413
  </tr>
1414
  <!-- / MainWP Child Site Stealth Mode -->
1415
- <?php $admin_blocking_plugins_support = $this->_plugin->settings()->get_admin_blocking_plugin_support(); ?>
1416
- <tr>
1417
- <th><label for="mwp_admin_blocking_support"><?php esc_html_e( 'Admin blocking plugins support', 'wp-security-audit-log' ); ?></label></th>
1418
- <td>
1419
- <fieldset>
1420
- <label for="mwp_admin_blocking_support">
1421
- <input type="checkbox" name="mwp_admin_blocking_support" value="yes" id="mwp_admin_blocking_support" <?php checked( $admin_blocking_plugins_support ); ?> <?php disabled( ! WpSecurityAuditLog::is_mainwp_active() || !$stealth_mode); ?>/>
1422
- <?php esc_html_e( 'Enable early plugin loading on sites that use admin blocking plugins', 'wp-security-audit-log' ); ?>
1423
- </label>
1424
- </fieldset>
1425
- </td>
1426
- </tr>
1427
- <!-- / Admin blocking plugins support -->
 
 
 
1428
  </tbody>
1429
  </table>
1430
 
@@ -1498,7 +1501,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1498
 
1499
  $admin_blocking_plugins_support = isset( $post_array['mwp_admin_blocking_support'] ) ? $post_array['mwp_admin_blocking_support'] : false;
1500
  if ( 'yes' === $admin_blocking_plugins_support ) {
1501
- $this->_plugin->settings()->set_admin_blocking_plugin_support(true);
1502
  }
1503
  } else {
1504
  $this->_plugin->settings()->deactivate_mainwp_child_stealth_mode();
@@ -1649,11 +1652,27 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1649
  // Filter $_GET array for security.
1650
  $get_array = filter_input_array( INPUT_GET );
1651
  $this->check_ajax_request_is_valid( $get_array );
1652
-
1653
- echo wp_json_encode( array_values( WSAL_ConstantManager::getSeverities() ) );
1654
  exit;
1655
  }
1656
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1657
  /**
1658
  * Create json array of all possible event types.
1659
  *
@@ -1667,7 +1686,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1667
 
1668
  $event_types = $this->_plugin->alerts->get_event_type_data();
1669
 
1670
- echo wp_json_encode( array_values( $event_types ) );
1671
  exit;
1672
  }
1673
 
@@ -1683,8 +1702,8 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1683
  $this->check_ajax_request_is_valid( $get_array );
1684
 
1685
  $event_objects = $this->_plugin->alerts->get_event_objects_data();
1686
-
1687
- echo wp_json_encode( array_values( $event_objects ) );
1688
  exit;
1689
  }
1690
 
@@ -1695,7 +1714,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1695
  * @since 4.3.3
1696
  */
1697
  public function ajax_get_all_event_ids() {
1698
-
1699
  $get_array = filter_input_array( INPUT_GET );
1700
  $this->check_ajax_request_is_valid( $get_array );
1701
 
@@ -1706,10 +1725,10 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
1706
  $alerts[] = (string) $details->code;
1707
  }
1708
 
1709
- echo wp_json_encode( $alerts );
1710
  exit;
1711
  }
1712
-
1713
  /**
1714
  * Method: Get CPTs ajax handle.
1715
  *
183
  * Method: Get View Weight.
184
  */
185
  public function GetWeight() {
186
+ return 8;
187
  }
188
 
189
  /**
406
  elementRef.checked = false;
407
  // Ensure the "no" option is reselected.
408
  jQuery('#delete_data_no').click();
409
+ }
410
  }
411
  }
412
 
1138
  private function tab_audit_log_save() {
1139
  // Get $_POST global array.
1140
  $post_array = filter_input_array( INPUT_POST );
1141
+
1142
  // Get pruning date.
1143
  $pruning_date = isset( $post_array['PruningDate'] ) ? (int) sanitize_text_field( $post_array['PruningDate'] ) : false;
1144
  $pruning_unit = isset( $post_array['pruning-unit'] ) ? sanitize_text_field( $post_array['pruning-unit'] ) : false;
1412
  </td>
1413
  </tr>
1414
  <!-- / MainWP Child Site Stealth Mode -->
1415
+ <?php $admin_blocking_plugins_support = $this->_plugin->settings()->get_admin_blocking_plugin_support(); ?>
1416
+ <tr>
1417
+ <th>
1418
+ <label for="mwp_admin_blocking_support"><?php esc_html_e( 'Admin blocking plugins support', 'wp-security-audit-log' ); ?></label>
1419
+ </th>
1420
+ <td>
1421
+ <fieldset>
1422
+ <label for="mwp_admin_blocking_support">
1423
+ <input type="checkbox" name="mwp_admin_blocking_support" value="yes"
1424
+ id="mwp_admin_blocking_support" <?php checked( $admin_blocking_plugins_support ); ?> <?php disabled( ! WpSecurityAuditLog::is_mainwp_active() || ! $stealth_mode ); ?>/>
1425
+ <?php esc_html_e( 'Enable early plugin loading on sites that use admin blocking plugins', 'wp-security-audit-log' ); ?>
1426
+ </label>
1427
+ </fieldset>
1428
+ </td>
1429
+ </tr>
1430
+ <!-- / Admin blocking plugins support -->
1431
  </tbody>
1432
  </table>
1433
 
1501
 
1502
  $admin_blocking_plugins_support = isset( $post_array['mwp_admin_blocking_support'] ) ? $post_array['mwp_admin_blocking_support'] : false;
1503
  if ( 'yes' === $admin_blocking_plugins_support ) {
1504
+ $this->_plugin->settings()->set_admin_blocking_plugin_support( true );
1505
  }
1506
  } else {
1507
  $this->_plugin->settings()->deactivate_mainwp_child_stealth_mode();
1652
  // Filter $_GET array for security.
1653
  $get_array = filter_input_array( INPUT_GET );
1654
  $this->check_ajax_request_is_valid( $get_array );
1655
+
1656
+ echo $this->filter_values_for_searched_term( array_values( WSAL_ConstantManager::getSeverities() ), $get_array['term'] );
1657
  exit;
1658
  }
1659
 
1660
+ /**
1661
+ * Filters values to return ones matching the desired term.
1662
+ *
1663
+ * @param array $items_to_filter
1664
+ * @param string $term
1665
+ * @return string
1666
+ */
1667
+ public function filter_values_for_searched_term( $items_to_filter, $term ) {
1668
+
1669
+ $result = array_filter( $items_to_filter, function( $value ) use ( $term ) {
1670
+ return strpos( strtolower( $value ), strtolower( $term ) ) !== false;
1671
+ });
1672
+
1673
+ return wp_json_encode( $result );
1674
+ }
1675
+
1676
  /**
1677
  * Create json array of all possible event types.
1678
  *
1686
 
1687
  $event_types = $this->_plugin->alerts->get_event_type_data();
1688
 
1689
+ echo $this->filter_values_for_searched_term( array_values( $event_types ), $get_array['term'] );
1690
  exit;
1691
  }
1692
 
1702
  $this->check_ajax_request_is_valid( $get_array );
1703
 
1704
  $event_objects = $this->_plugin->alerts->get_event_objects_data();
1705
+
1706
+ echo $this->filter_values_for_searched_term( array_values( $event_objects ), $get_array['term'] );
1707
  exit;
1708
  }
1709
 
1714
  * @since 4.3.3
1715
  */
1716
  public function ajax_get_all_event_ids() {
1717
+
1718
  $get_array = filter_input_array( INPUT_GET );
1719
  $this->check_ajax_request_is_valid( $get_array );
1720
 
1725
  $alerts[] = (string) $details->code;
1726
  }
1727
 
1728
+ echo $this->filter_values_for_searched_term( array_values( $alerts ), $get_array['term'] );
1729
  exit;
1730
  }
1731
+
1732
  /**
1733
  * Method: Get CPTs ajax handle.
1734
  *
classes/Views/SetupWizard.php CHANGED
@@ -267,11 +267,12 @@ final class WSAL_Views_SetupWizard {
267
 
268
  $installer_script_data = array(
269
  'ajaxURL' => admin_url( 'admin-ajax.php' ),
270
- 'installing' => __( 'Installing, please wait', 'wp-security-audit-log' ),
271
- 'already_installed' => __( 'Already installed', 'wp-security-audit-log' ),
272
- 'installed' => __( 'Extension installed', 'wp-security-audit-log' ),
273
- 'activated' => __( 'Extension activated', 'wp-security-audit-log' ),
274
- 'failed' => __( 'Install failed', 'wp-security-audit-log' ),
 
275
  );
276
  wp_localize_script( 'wsal-common', 'wsalCommonData', $installer_script_data );
277
 
@@ -426,7 +427,7 @@ final class WSAL_Views_SetupWizard {
426
  private function wsal_step_welcome() {
427
  // dismiss the setup modal in case if not already done
428
  if ( ! $this->wsal->GetGlobalBooleanSetting( 'setup-modal-dismissed', false ) ) {
429
- $this->wsal->SetGlobalBooleanSetting( 'setup-modal-dismissed', true );
430
  }
431
  ?>
432
  <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>
267
 
268
  $installer_script_data = array(
269
  'ajaxURL' => admin_url( 'admin-ajax.php' ),
270
+ 'installing' => esc_html__( 'Installing, please wait', 'wp-security-audit-log' ),
271
+ 'already_installed' => esc_html__( 'Already installed', 'wp-security-audit-log' ),
272
+ 'installed' => esc_html__( 'Extension installed', 'wp-security-audit-log' ),
273
+ 'activated' => esc_html__( 'Extension activated', 'wp-security-audit-log' ),
274
+ 'failed' => esc_html__( 'Install failed', 'wp-security-audit-log' ),
275
+ 'reloading_page' => esc_html__( 'Reloading page', 'wp-security-audit-log' )
276
  );
277
  wp_localize_script( 'wsal-common', 'wsalCommonData', $installer_script_data );
278
 
427
  private function wsal_step_welcome() {
428
  // dismiss the setup modal in case if not already done
429
  if ( ! $this->wsal->GetGlobalBooleanSetting( 'setup-modal-dismissed', false ) ) {
430
+ $this->wsal->SetGlobalBooleanSetting( 'setup-modal-dismissed', true, true );
431
  }
432
  ?>
433
  <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>
classes/Views/ToggleAlerts.php CHANGED
@@ -45,7 +45,7 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
45
  * Method: Get View Weight.
46
  */
47
  public function GetWeight() {
48
- return 2;
49
  }
50
 
51
  /**
@@ -83,6 +83,9 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
83
  // Save enabled front end events.
84
  WSAL_Settings::set_frontend_events( $frontend_events );
85
 
 
 
 
86
  $enabled = array_map( 'intval', $post_array['alert'] );
87
  $disabled = array();
88
  $registered_alerts = $this->_plugin->alerts->GetAlerts();
@@ -109,15 +112,10 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
109
  wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-security-audit-log' ) );
110
  }
111
 
112
- $alert = new WSAL_Alert(); // IDE type hinting.
113
- $grouped_alerts = $this->_plugin->alerts->GetCategorizedAlerts( false );
114
- $safe_names = array_map( array( $this, 'GetSafeCatgName' ), array_keys( $grouped_alerts ) );
115
- $safe_names = array_combine( array_keys( $grouped_alerts ), $safe_names );
116
-
117
  // Filter $_POST array.
118
  $post_array = filter_input_array( INPUT_POST );
119
 
120
- if ( isset( $post_array['submit'] ) && isset( $post_array['alert'] ) ) {
121
  check_admin_referer( 'wsal-togglealerts' );
122
  try {
123
  $this->save();
@@ -149,6 +147,11 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
149
  }
150
  }
151
 
 
 
 
 
 
152
  $disabled_events = $this->_plugin->GetGlobalSetting( 'disabled-alerts' ); // Get disabled events.
153
  $disabled_events = explode( ',', $disabled_events );
154
 
@@ -165,7 +168,7 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
165
  // Allow further items to be added externally.
166
  $subcat_alerts = apply_filters( 'wsal_togglealerts_sub_category_events', $subcat_alerts );
167
 
168
- $obsolete_events = array( 9999, 2126 );
169
  $obsolete_events = apply_filters( 'wsal_togglealerts_obsolete_events', $obsolete_events );
170
 
171
  // check if the disabled events are enforced from the MainWP master site
@@ -173,380 +176,280 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
173
  $enforced_settings = $settings->get_mainwp_enforced_settings();
174
  $disabled_events_enforced_by_mainwp = array_key_exists( 'disabled_events', $enforced_settings ) && ! empty( $enforced_settings[ 'disabled_events' ]);
175
  ?>
176
- <p>
177
- <form method="post" id="wsal-alerts-level">
178
- <?php wp_nonce_field( 'wsal-log-level', 'wsal-log-level-nonce' ); ?>
179
- <fieldset>
180
- <label for="wsal-log-level"><?php esc_html_e( 'Log Level: ', 'wp-security-audit-log' ); ?></label>
181
- <select name="wsal-log-level" id="wsal-log-level" onchange="this.form.submit()"<?php if ( $disabled_events_enforced_by_mainwp ): ?> disabled="disabled"<?php endif; ?>>
182
- <?php foreach ( $log_level_options as $log_level_id => $log_level_label ): ?>
183
- <option value="<?php echo $log_level_id; ?>" <?php echo selected( $log_level, $log_level_id ); ?>><?php echo $log_level_label; ?></option>
184
- <?php endforeach; ?>
185
- </select>
186
- <p class="description">
187
- <?php echo wp_kses( __( 'Use the Log level drop down menu above to use one of our preset log levels. Alternatively you can enable or disable any of the individual events from the below tabs. Refer to <a href="https://wpactivitylog.com/support/kb/list-wordpress-activity-log-event-ids/" target="_blank">the complete list of WordPress activity log event IDs</a> for reference on all the events the plugin can keep a log of.', 'wp-security-audit-log' ), $this->_plugin->allowed_html_tags ); ?>
188
- </p>
189
- </fieldset>
190
- </form>
191
- </p>
192
  <h2 id="wsal-tabs" class="nav-tab-wrapper">
193
- <?php foreach ( $safe_names as $name => $safe ) : ?>
194
- <a href="#tab-<?php echo esc_attr( $safe ); ?>" class="nav-tab"><?php echo esc_html( $name ); ?></a>
195
- <?php endforeach; ?>
196
  <a href="#tab-third-party-plugins" class="nav-tab">
197
  <?php esc_html_e( 'Third party plugins', 'wp-security-audit-log' ); ?>
198
  </a>
199
- </h2>
200
- <form id="audit-log-viewer" method="post">
201
- <input type="hidden" name="page" value="<?php echo isset( $_GET['page'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['page'] ) ) ) : false; ?>" />
202
- <?php wp_nonce_field( 'wsal-togglealerts' ); ?>
203
-
204
- <div class="nav-tabs">
205
- <?php foreach ( $grouped_alerts as $name => $group ) : ?>
206
- <div class="wsal-tab" id="tab-<?php echo esc_attr( $safe_names[ $name ] ); ?>">
207
- <h2 class="nav-tab-wrapper wsal-sub-tabs">
208
- <?php
209
- foreach ( $group as $subname => $alerts ) :
210
- // Skip Pages and CPTs section.
211
- if ( __( 'Custom Post Types', 'wp-security-audit-log' ) === $subname || __( 'Pages', 'wp-security-audit-log' ) === $subname ) {
212
- continue;
213
- }
214
- ?>
215
- <a href="#tab-<?php echo esc_attr( $this->GetSafeCatgName( $subname ) ); ?>"
216
- class="nav-tab"
217
- data-parent="tab-<?php echo esc_attr( $safe_names[ $name ] ); ?>">
218
- <?php echo esc_html( $subname ); ?>
219
- </a>
220
  <?php endforeach; ?>
221
- </h2>
222
- <?php
223
- foreach ( $group as $subname => $alerts ) {
224
- $active = array();
225
- $allactive = true;
226
- /** @var WSAL_Alert $alert */
227
- foreach ( $alerts as $alert ) {
228
- if ( $alert->code <= 0006 ) {
229
- continue; // <- Ignore php alerts.
230
- }
231
- if ( in_array( $alert->code, $obsolete_events, true ) ) {
232
- continue; // <- Ignore promo alerts.
233
- }
234
- $active[ $alert->code ] = $this->_plugin->alerts->IsEnabled( $alert->code );
235
- if ( ! $active[ $alert->code ] ) {
236
- $allactive = false;
237
- }
238
- }
239
-
240
- // Disabled alerts.
241
- $disable_inputs = '';
242
-
243
- // Skip Pages and CPTs section.
244
- if ( __( 'Custom Post Types', 'wp-security-audit-log' ) === $subname || __( 'Pages', 'wp-security-audit-log' ) === $subname ) {
245
- continue;
246
- } elseif (
247
- 'bbPress Forums' === $name
248
- || 'WooCommerce' === $name
249
- || 'Yoast SEO' === $name
250
- || 'Multisite Network Sites' === $name
251
- || 'User Accounts' === $name
252
- ) {
253
- switch ( $name ) {
254
- case 'User Accounts':
255
- if ( 'Multisite User Profiles' === $subname ) {
256
- // Check if this is a multisite.
257
- if ( ! is_multisite() ) {
258
- $disable_inputs = 'disabled';
259
- }
260
- }
261
- break;
262
-
263
- case 'WooCommerce':
264
- case 'WooCommerce Products':
265
- // Check if WooCommerce plugin exists.
266
- if ( ! WpSecurityAuditLog::is_woocommerce_active() ) {
267
- $disable_inputs = 'disabled';
268
- }
269
- break;
270
-
271
- case 'Yoast SEO':
272
- // Check if Yoast SEO plugin exists.
273
- if ( ! WpSecurityAuditLog::is_wpseo_active() ) {
274
- $disable_inputs = 'disabled';
275
- }
276
- break;
277
-
278
- case 'Multisite Network Sites':
279
- // Disable if not multisite.
280
- if ( ! is_multisite() ) {
281
- $disable_inputs = 'disabled';
282
- }
283
- break;
284
-
285
- default:
286
- break;
287
- }
288
- }
289
-
290
- if ( $disabled_events_enforced_by_mainwp ) {
291
- $disable_inputs = 'disabled';
292
- }
293
- ?>
294
- <table class="wp-list-table wsal-tab widefat fixed wsal-sub-tab" cellspacing="0" id="tab-<?php echo esc_attr( $this->GetSafeCatgName( $subname ) ); ?>">
295
  <thead>
296
  <tr>
297
- <th width="48"><input type="checkbox" <?php checked( $allactive ); ?> <?php echo esc_attr( $disable_inputs ); ?> /></th>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  <th width="80"><?php esc_html_e( 'Code', 'wp-security-audit-log' ); ?></th>
299
  <th width="100"><?php esc_html_e( 'Severity', 'wp-security-audit-log' ); ?></th>
300
  <th><?php esc_html_e( 'Description', 'wp-security-audit-log' ); ?></th>
 
 
301
  </tr>
302
  </thead>
303
  <tbody>
304
- <?php if ( __( 'Content', 'wp-security-audit-log' ) === $subname ) : ?>
305
- <tr>
306
- <td colspan="4">
307
- <p class="wsal-tab-help description"><?php echo wp_kses( __( '<strong>Note:</strong> Post refers to any type of content, i.e. blog post, page or a post with a custom post type.', 'wp-security-audit-log' ), $this->_plugin->allowed_html_tags ); ?></p>
308
- </td>
309
- </tr>
310
- <?php elseif ( __( 'WooCommerce', 'wp-security-audit-log' ) === $subname || __( 'WooCommerce Products', 'wp-security-audit-log' ) === $subname ) : ?>
311
- <?php if ( ! empty( $disable_inputs ) ) : ?>
312
- <tr>
313
- <td colspan="4">
314
- <p class="wsal-tab-help wsal-tab-notice description"><?php esc_html_e( 'The plugin WooCommerce is not installed on your website so these events have been disabled.', 'wp-security-audit-log' ); ?></p>
315
- </td>
316
- </tr>
317
- <?php endif; ?>
318
- <?php if ( __( 'WooCommerce Products', 'wp-security-audit-log' ) === $subname ) : ?>
319
- <tr>
320
- <td colspan="4">
321
- <h3 class="sub-category"><?php esc_html_e( 'Products', 'wp-security-audit-log' ); ?></h3>
322
- </td>
323
- </tr>
324
- <?php endif; ?>
325
- <tr>
326
- <td colspan="4">
327
- <h3 class="sub-category"><?php esc_html_e( 'Post Changes', 'wp-security-audit-log' ); ?></h3>
328
- </td>
329
- </tr>
330
- <?php elseif ( __( 'MultiSite', 'wp-security-audit-log' ) === $subname ) : ?>
331
- <?php if ( ! empty( $disable_inputs ) ) : ?>
332
- <tr>
333
- <td colspan="4">
334
- <p class="wsal-tab-help wsal-tab-notice description"><?php esc_html_e( 'Your website is a single site so the multisite events have been disabled.', 'wp-security-audit-log' ); ?></p>
335
- </td>
336
- </tr>
337
- <?php endif; ?>
338
- <?php elseif ( __( 'Other User Activity', 'wp-security-audit-log' ) === $subname ) : ?>
339
- <tr>
340
- <td colspan="4">
341
- <h3 class="sub-category"><?php esc_html_e( 'Logins & Logouts', 'wp-security-audit-log' ); ?></h3>
342
- </td>
343
- </tr>
344
- <?php endif; ?>
345
-
346
  <?php
347
- // Events sections loop.
348
- foreach ( $alerts as $alert ) {
349
- if ( $alert->code <= 0006 ) {
350
- continue; // <- Ignore php alerts.
351
- }
352
 
353
- if ( __( 'Monitor File Changes', 'wp-security-audit-log' ) === $subname && ! defined( 'WFCM_PLUGIN_FILE' ) ) {
354
- break;
355
- }
 
 
 
 
 
 
 
 
356
 
357
- if ( in_array( $alert->code, $obsolete_events, true ) ) {
358
- continue; // <- Ignore promo alerts.
359
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
- $attrs = '';
362
- switch ( true ) {
363
- case ! $alert->mesg:
364
- $attrs = 'title="' . __( 'Not Implemented', 'wp-security-audit-log' ) . '" class="alert-incomplete"';
365
- break;
366
- case false:
367
- $attrs = 'title="' . __( 'Not Available', 'wp-security-audit-log' ) . '" class="alert-unavailable"';
368
- break;
369
- default:
370
- // fallback for any other cases would go here
371
- break;
372
- }
373
- if ( in_array( $alert->code, $subcat_alerts, true ) ) {
374
- ?>
375
- <tr>
376
- <td colspan="4">
377
- <h3 class="sub-category">
378
- <?php
379
- $subcat_title = '';
380
- switch ( $alert->code ) {
381
- case 1000:
382
- $subcat_title = esc_html__( 'User Logins/Logouts', 'wp-security-audit-log' );
383
- break;
384
- case 1004:
385
- $subcat_title = esc_html__( 'User Sessions', 'wp-security-audit-log' );
386
- break;
387
- case 2010:
388
- $subcat_title = esc_html__( 'Files', 'wp-security-audit-log' );
389
- break;
390
- case 2011:
391
- $subcat_title = esc_html__( 'Post Settings', 'wp-security-audit-log' );
392
- break;
393
- default:
394
- break;
395
  }
 
396
 
397
- // Allow further titles to be added externally.
398
- $subcat_title = apply_filters( 'wsal_togglealerts_sub_category_titles', $subcat_title, $alert->code );
399
- if ( $subcat_title ) {
400
- echo esc_html( $subcat_title );
 
401
  }
402
- ?>
403
- </h3>
404
- </td>
405
- </tr>
406
- <?php
407
- }
408
- ?>
409
- <tr <?php echo esc_attr( $attrs ); ?>>
410
- <th>
411
- <input
412
- name="alert[]"
413
- type="checkbox"
414
- class="alert"
415
- <?php checked( $active[ $alert->code ] ); ?>
416
- value="<?php echo esc_attr( (int) $alert->code ); ?>"
417
- <?php
418
- if ( ! empty( $disable_inputs ) ) {
419
- echo esc_attr( $disable_inputs );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
421
  ?>
422
- <?php echo ( __( 'File Changes', 'wp-security-audit-log' ) === $subname ) ? 'onclick="wsal_toggle_file_changes(this)"' : false; ?>
423
- />
424
- </th>
425
- <td><?php echo esc_html( str_pad( $alert->code, 4, '0', STR_PAD_LEFT ) ); ?></td>
426
- <td>
427
- <?php
428
- $severity_obj = $this->_plugin->constants->GetConstantBy( 'value', $alert->severity );
429
- if ( 'E_CRITICAL' === $severity_obj->name ) {
430
- esc_html_e( 'Critical', 'wp-security-audit-log' );
431
- } elseif ( 'E_WARNING' === $severity_obj->name ) {
432
- esc_html_e( 'Warning', 'wp-security-audit-log' );
433
- } elseif ( 'E_NOTICE' === $severity_obj->name ) {
434
- esc_html_e( 'Notification', 'wp-security-audit-log' );
435
- } elseif ( 'WSAL_CRITICAL' === $severity_obj->name ) {
436
- esc_html_e( 'Critical', 'wp-security-audit-log' );
437
- } elseif ( 'WSAL_HIGH' === $severity_obj->name ) {
438
- esc_html_e( 'High', 'wp-security-audit-log' );
439
- } elseif ( 'WSAL_MEDIUM' === $severity_obj->name ) {
440
- esc_html_e( 'Medium', 'wp-security-audit-log' );
441
- } elseif ( 'WSAL_LOW' === $severity_obj->name ) {
442
- esc_html_e( 'Low', 'wp-security-audit-log' );
443
- } elseif ( 'WSAL_INFORMATIONAL' === $severity_obj->name ) {
444
- esc_html_e( 'Informational', 'wp-security-audit-log' );
445
- } else {
446
- esc_html_e( 'Notification', 'wp-security-audit-log' );
447
  }
448
- ?>
449
- </td>
450
- <td><?php echo esc_html( $alert->desc ); ?></td>
451
- </tr>
452
- <?php
453
 
454
- if ( 4000 === $alert->code ) {
455
- $frontend_events = WSAL_Settings::get_frontend_events();
456
- ?>
457
- <tr>
458
- <td></td>
459
- <td><input type="checkbox" name="frontend-events[register]" id="frontend-events-register" value="1" <?php checked( $frontend_events['register'] ); ?>></td>
460
- <td colspan="2">
461
- <label for="frontend-events-register"><?php esc_html_e( 'Keep a log when a visitor registers a user on the website. Only enable this if you allow visitors to register as users on your website. User registration is disabled by default in WordPress.', 'wp-security-audit-log' ); ?></label>
462
- </td>
463
- </tr>
464
- <?php
465
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
 
467
- if ( 1002 === $alert->code ) {
468
- $log_failed_login_limit = (int) $this->_plugin->GetGlobalSetting( 'log-failed-login-limit', 10 );
469
- $log_failed_login_limit = ( -1 === $log_failed_login_limit ) ? '0' : $log_failed_login_limit;
470
- ?>
471
- <tr>
472
- <td></td>
473
- <td><input name="log_failed_login_limit" type="number" class="check_visitor_log" value="<?php echo esc_attr( $log_failed_login_limit ); ?>"></td>
474
- <td colspan="2">
475
- <?php esc_html_e( 'Number of login attempts to log. Enter 0 to log all failed login attempts. (By default the plugin only logs up to 10 failed login because the process can be very resource intensive in case of a brute force attack)', 'wp-security-audit-log' ); ?>
476
- </td>
477
- </tr>
478
- <?php
479
- }
480
- if ( 1003 === $alert->code ) {
481
- $log_visitor_failed_login_limit = (int) $this->_plugin->GetGlobalSetting( 'log-visitor-failed-login-limit', 10 );
482
- $log_visitor_failed_login_limit = ( -1 === $log_visitor_failed_login_limit ) ? '0' : $log_visitor_failed_login_limit;
483
- ?>
484
- <tr>
485
- <td></td>
486
- <td><input name="log_visitor_failed_login_limit" type="number" class="check_visitor_log" value="<?php echo esc_attr( $log_visitor_failed_login_limit ); ?>"></td>
487
- <td colspan="2">
488
- <p><?php esc_html_e( 'Number of login attempts to log. Enter 0 to log all failed login attempts. (By default the plugin only logs up to 10 failed login because the process can be very resource intensive in case of a brute force attack)', 'wp-security-audit-log' ); ?></p>
489
- </td>
490
- </tr>
491
- <?php
492
- }
493
 
494
- if ( 1003 === $alert->code ) {
495
- $frontend_events = WSAL_Settings::get_frontend_events();
496
- ?>
497
- <tr>
498
- <th>
499
- <input type="checkbox" name="frontend-events[login]" id="frontend-events-login" value="1" <?php checked( $frontend_events['login'] ); ?>>
500
- </th>
501
- <td colspan="3">
502
- <label for="frontend-events-login"><?php esc_html_e( 'Keep a log of user log in activity on custom login forms (such as WooCommerce & membership plugins)', 'wp-security-audit-log' ); ?></label>
503
- </td>
504
- </tr>
505
- <?php
506
  }
507
-
508
- do_action( 'wsal_togglealerts_append_content_to_toggle', $alert->code );
509
  }
510
 
511
- // File integrity scan link.
512
- if ( __( 'Monitor File Changes', 'wp-security-audit-log' ) === $subname && ! defined( 'WFCM_PLUGIN_FILE' ) ) :
513
- $redirect_args = array(
514
- 'page' => 'wsal-settings',
515
- 'tab' => 'file-changes',
516
- );
517
- ?>
518
- <tr>
519
- <td>
520
- <div class="addon-wrapper">
521
- <img src="<?php echo trailingslashit( WSAL_BASE_URL ) . 'img/help/website-file-changes-monitor.jpg'; ?>">
522
- <h4><?php echo esc_html__( 'Website File Changes Monitor', 'wp-security-audit-log' ); ?></h4>
523
- <p><?php echo esc_html__( 'To keep a log of file changes please install Website File Changes Monitor, a plugin which is also developed by us.', 'wp-security-audit-log' ); ?></p><br>
524
- <p><button class="install-addon button button-primary" data-nonce="<?php echo esc_attr( wp_create_nonce( 'wsal-install-addon' ) ); ?>" data-plugin-slug="website-file-changes-monitor/website-file-changes-monitor.php" data-plugin-download-url="https://downloads.wordpress.org/plugin/website-file-changes-monitor.latest-stable.zip"><?php _e( 'Install plugin now', 'wp-security-audit-log' ); ?></button><span class="spinner" style="display: none; visibility: visible; float: none; margin: 0 0 0 8px;"></span> <a href="https://wpactivitylog.com/support/kb/wordpress-files-changes-warning-activity-logs/?utm_source=plugin&utm_medium=referral&utm_campaign=WSAL&utm_content=settings+pages" rel="noopener noreferrer" target="_blank" style="margin-left: 15px;"><?php echo esc_html__( 'Learn More', 'wp-security-audit-log' ); ?></a></p>
525
- </div>
526
- </td>
527
- </tr>
528
- <style type="text/css">
529
- #tab-monitor-file-changes thead {
530
- display: none;
531
- }
532
- </style>
533
- <?php
534
- endif;
535
  ?>
536
  </tbody>
537
  </table>
538
- <?php
539
- }
540
- ?>
541
- </div>
542
- <?php endforeach; ?>
543
- <?php
544
- $addons = new WSAL_PluginInstallAndActivate();
545
- $addons->render();
546
- ?>
547
  </div>
548
- <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php echo esc_attr( __( 'Save Changes', 'wp-security-audit-log' ) ); ?>"></p>
549
- </form>
 
 
 
 
550
 
551
  <?php if ( ! empty( $log_level_to_set ) ) : ?>
552
  <!-- Log level updated modal -->
@@ -594,6 +497,16 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
594
  // Remodal styles.
595
  wp_enqueue_style( 'wsal-remodal', WSAL_BASE_URL . 'css/remodal.css', array(), '1.1.1' );
596
  wp_enqueue_style( 'wsal-remodal-theme', WSAL_BASE_URL . 'css/remodal-default-theme.css', array(), '1.1.1' );
 
 
 
 
 
 
 
 
 
 
597
  ?>
598
  <style type="text/css">
599
  .wsal-tab {
@@ -672,28 +585,107 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
672
  }
673
  .addon-wrapper {
674
  flex: 1 0 30%;
675
- margin: 0 5px 11px 5px;
676
- border: 1px solid #eee;
677
- padding: 20px;
678
  }
679
  .addon-wrapper:hover {
680
  border: 1px solid #ccc;
681
  }
682
  .addon-wrapper .button-primary {
683
  position: relative;
684
- padding: 13px 26px !important;
685
- font-weight: 500;
686
- font-size: 16px;
687
- line-height: 1;
688
- color: white;
689
- background: #007cba;
690
- border: none;
691
- outline: none;
692
- overflow: hidden;
693
- cursor: pointer;
694
- filter: drop-shadow(0 2px 8px rgba(39, 94, 254, 0.32));
695
- transition: 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
696
- line-height: 1 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  }
698
  </style>
699
  <?php
@@ -707,12 +699,53 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
707
  wp_enqueue_script(
708
  'wsal-remodal-js',
709
  WSAL_BASE_URL . 'js/remodal.min.js',
710
- array(),
711
  '1.1.1',
712
  true
713
  );
 
 
 
 
 
 
 
 
 
 
714
  ?>
715
  <script type="text/javascript">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  jQuery(document).ready(function(){
717
  var scrollHeight = jQuery(document).scrollTop();
718
  // tab handling code
@@ -735,12 +768,12 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
735
  }, 1);
736
  });
737
  // checkbox handling code
738
- jQuery('.wsal-tab>thead>tr>th>:checkbox').change(function(){
739
- jQuery(this).parents('table:first').find('tbody>tr>th>:checkbox').attr('checked', this.checked);
740
  });
741
- jQuery('.wsal-tab>tbody>tr>th>:checkbox').change(function(){
742
  var allchecked = jQuery(this).parents('tbody:first').find('th>:checkbox:not(:checked)').length === 0;
743
- jQuery(this).parents('table:first').find('thead>tr>th:first>:checkbox:first').attr('checked', allchecked);
744
  });
745
 
746
  var hashlink = jQuery('#wsal-tabs>a[href="' + location.hash + '"]');
@@ -756,6 +789,63 @@ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
756
  jQuery('#wsal-tabs>a:first').click();
757
  jQuery('.wsal-sub-tabs>a:first').click();
758
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
759
  });
760
 
761
  if (typeof toggle_modal !== 'undefined') {
45
  * Method: Get View Weight.
46
  */
47
  public function GetWeight() {
48
+ return 9;
49
  }
50
 
51
  /**
83
  // Save enabled front end events.
84
  WSAL_Settings::set_frontend_events( $frontend_events );
85
 
86
+ // Ensure we attempt to save even if eveything is disabled.
87
+ $post_array['alert'] = ( isset( $post_array['alert'] ) ) ? $post_array['alert'] : [];
88
+
89
  $enabled = array_map( 'intval', $post_array['alert'] );
90
  $disabled = array();
91
  $registered_alerts = $this->_plugin->alerts->GetAlerts();
112
  wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-security-audit-log' ) );
113
  }
114
 
 
 
 
 
 
115
  // Filter $_POST array.
116
  $post_array = filter_input_array( INPUT_POST );
117
 
118
+ if ( isset( $post_array['submit'] ) ) {
119
  check_admin_referer( 'wsal-togglealerts' );
120
  try {
121
  $this->save();
147
  }
148
  }
149
 
150
+ $alert = new WSAL_Alert(); // IDE type hinting.
151
+ $grouped_alerts = $this->_plugin->alerts->GetCategorizedAlerts( false );
152
+ $safe_names = array_map( array( $this, 'GetSafeCatgName' ), array_keys( $grouped_alerts ) );
153
+ $safe_names = array_combine( array_keys( $grouped_alerts ), $safe_names );
154
+
155
  $disabled_events = $this->_plugin->GetGlobalSetting( 'disabled-alerts' ); // Get disabled events.
156
  $disabled_events = explode( ',', $disabled_events );
157
 
168
  // Allow further items to be added externally.
169
  $subcat_alerts = apply_filters( 'wsal_togglealerts_sub_category_events', $subcat_alerts );
170
 
171
+ $obsolete_events = array( 9999, 2126, 99999 );
172
  $obsolete_events = apply_filters( 'wsal_togglealerts_obsolete_events', $obsolete_events );
173
 
174
  // check if the disabled events are enforced from the MainWP master site
176
  $enforced_settings = $settings->get_mainwp_enforced_settings();
177
  $disabled_events_enforced_by_mainwp = array_key_exists( 'disabled_events', $enforced_settings ) && ! empty( $enforced_settings[ 'disabled_events' ]);
178
  ?>
179
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  <h2 id="wsal-tabs" class="nav-tab-wrapper">
181
+ <a href="#tab-all" class="nav-tab"><?php esc_html_e( 'Enable/Disable alerts', 'wp-security-audit-log' ); ?></a>
 
 
182
  <a href="#tab-third-party-plugins" class="nav-tab">
183
  <?php esc_html_e( 'Third party plugins', 'wp-security-audit-log' ); ?>
184
  </a>
185
+ </h2>
186
+
187
+ <div class="nav-tabs">
188
+
189
+ <div id="tab-all" class="wsal-tab widefat fixed">
190
+
191
+ <form method="post" id="wsal-alerts-level">
192
+ <?php wp_nonce_field( 'wsal-log-level', 'wsal-log-level-nonce' ); ?>
193
+ <fieldset>
194
+ <label for="wsal-log-level"><?php esc_html_e( 'Log Level: ', 'wp-security-audit-log' ); ?></label>
195
+ <select name="wsal-log-level" id="wsal-log-level" onchange="this.form.submit()"<?php if ( $disabled_events_enforced_by_mainwp ): ?> disabled="disabled"<?php endif; ?>>
196
+ <?php foreach ( $log_level_options as $log_level_id => $log_level_label ): ?>
197
+ <option value="<?php echo $log_level_id; ?>" <?php echo selected( $log_level, $log_level_id ); ?>><?php echo $log_level_label; ?></option>
 
 
 
 
 
 
 
 
198
  <?php endforeach; ?>
199
+ </select>
200
+ <p class="description">
201
+ <?php echo wp_kses( __( 'Use the Log level drop down menu above to use one of our preset log levels. Alternatively you can enable or disable any of the individual events from the below tabs. Refer to <a href="https://wpactivitylog.com/support/kb/list-wordpress-activity-log-event-ids/" target="_blank">the complete list of WordPress activity log event IDs</a> for reference on all the events the plugin can keep a log of.', 'wp-security-audit-log' ), $this->_plugin->allowed_html_tags ); ?>
202
+ </p>
203
+ </fieldset>
204
+ </form>
205
+
206
+ <form id="audit-log-viewer" method="post">
207
+
208
+ <input type="hidden" name="page" value="<?php echo isset( $_GET['page'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['page'] ) ) ) : false; ?>" />
209
+ <?php wp_nonce_field( 'wsal-togglealerts' ); ?>
210
+
211
+ <div id="flitering-options-wrapper">
212
+ <div class="search">
213
+ <span class="search-title"><?php esc_html_e( 'Choose your query and enter your search term', 'wp-security-audit-log' ); ?></span>
214
+ <select name="searchingFor" id="search-subject" class="search-input-style">
215
+ <option value="code"><?php esc_html_e( 'Event code', 'wp-security-audit-log' ); ?></option>
216
+ <option value="severity"><?php esc_html_e( 'Severity', 'wp-security-audit-log' ); ?></option>
217
+ <option value="desc"><?php esc_html_e( 'Description', 'wp-security-audit-log' ); ?></option>
218
+ </select>
219
+ <input type="text" id="query-input" class="search-input-style" onkeyup="filterEventTable()" placeholder="Enter search term">
220
+ </div>
221
+ <div class="center-bit"><span><?php esc_html_e( 'OR', 'wp-security-audit-log' ); ?></span></div>
222
+ <div class="choose">
223
+ <span class="search-title"><?php esc_html_e( 'Choose a category', 'wp-security-audit-log' ); ?></span>
224
+ <select name="catFilter" id="filter-cat" class="search-input-style">
225
+ <option value="all"><?php esc_html_e( 'All categories', 'wp-security-audit-log' ); ?></option>
226
+ </select>
227
+ </div>
228
+ </div>
229
+
230
+ <table id="event-toggle-table">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  <thead>
232
  <tr>
233
+ <?php
234
+ foreach ( $grouped_alerts as $name => $group ) {
235
+ foreach ( $group as $subname => $alerts ) {
236
+ $active = array();
237
+ $allactive = true;
238
+ /** @var WSAL_Alert $alert */
239
+ foreach ( $alerts as $alert ) {
240
+ if ( $alert->code <= 0006 ) {
241
+ continue; // <- Ignore php alerts.
242
+ }
243
+ if ( in_array( $alert->code, $obsolete_events, true ) ) {
244
+ continue; // <- Ignore promo alerts.
245
+ }
246
+ $active[ $alert->code ] = $this->_plugin->alerts->IsEnabled( $alert->code );
247
+ if ( ! $active[ $alert->code ] ) {
248
+ $allactive = false;
249
+ }
250
+ }
251
+ }
252
+ }
253
+ ?>
254
+ <th width="48"><input type="checkbox" <?php checked( $allactive ); ?> /></th>
255
  <th width="80"><?php esc_html_e( 'Code', 'wp-security-audit-log' ); ?></th>
256
  <th width="100"><?php esc_html_e( 'Severity', 'wp-security-audit-log' ); ?></th>
257
  <th><?php esc_html_e( 'Description', 'wp-security-audit-log' ); ?></th>
258
+ <th style="display: none;"></th>
259
+ <th style="display: none;"></th>
260
  </tr>
261
  </thead>
262
  <tbody>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  <?php
 
 
 
 
 
264
 
265
+ foreach ( $grouped_alerts as $name => $group ) {
266
+ foreach ( $group as $subname => $alerts ) {
267
+ $active = array();
268
+ $allactive = true;
269
+ /** @var WSAL_Alert $alert */
270
+ foreach ( $alerts as $alert ) {
271
+ $active[ $alert->code ] = $this->_plugin->alerts->IsEnabled( $alert->code );
272
+ if ( ! $active[ $alert->code ] ) {
273
+ $allactive = false;
274
+ }
275
+ }
276
 
277
+ // Disabled alerts.
278
+ $disable_inputs = '';
279
+ $disabled_message = '';
280
+
281
+ // Skip Pages and CPTs section.
282
+ if ( __( 'Custom Post Types', 'wp-security-audit-log' ) === $subname || __( 'Pages', 'wp-security-audit-log' ) === $subname ) {
283
+ continue;
284
+ } elseif (
285
+ 'bbPress Forums' === $name
286
+ || 'WooCommerce' === $name
287
+ || 'Yoast SEO' === $name
288
+ || 'Multisite Network Sites' === $name
289
+ || 'User Accounts' === $name
290
+ ) {
291
+ switch ( $name ) {
292
+ case 'User Accounts':
293
+ if ( 'Multisite User Profiles' === $subname ) {
294
+ // Check if this is a multisite.
295
+ if ( ! is_multisite() ) {
296
+ $disable_inputs = 'disabled';
297
+ }
298
+ }
299
+ break;
300
+
301
+ case 'WooCommerce':
302
+ case 'WooCommerce Products':
303
+ // Check if WooCommerce plugin exists.
304
+ if ( ! WpSecurityAuditLog::is_woocommerce_active() ) {
305
+ $disable_inputs = 'disabled';
306
+ $disabled_message = esc_html__( 'Please activate WooCommerce to enable this alert', 'wp-security-audit-log' );
307
+ }
308
+ break;
309
 
310
+ case 'Yoast SEO':
311
+ // Check if Yoast SEO plugin exists.
312
+ if ( ! WpSecurityAuditLog::is_wpseo_active() ) {
313
+ $disable_inputs = 'disabled';
314
+ $disabled_message = esc_html__( 'Please activate the Yoast SEO Plugin', 'wp-security-audit-log' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  }
316
+ break;
317
 
318
+ case 'Multisite Network Sites':
319
+ // Disable if not multisite.
320
+ if ( ! is_multisite() ) {
321
+ $disable_inputs = 'disabled';
322
+ $disabled_message = esc_html__( 'Your website is a single site so the multisite events have been disabled.', 'wp-security-audit-log' );
323
  }
324
+ break;
325
+
326
+ default:
327
+ break;
328
+ }
329
+ }
330
+
331
+ if ( $disabled_events_enforced_by_mainwp ) {
332
+ $disable_inputs = 'disabled';
333
+ }
334
+
335
+ foreach ( $alerts as $alert ) {
336
+
337
+ if ( $alert->code <= 0006 ) {
338
+ continue; // <- Ignore php alerts.
339
+ }
340
+ if ( in_array( $alert->code, $obsolete_events, true ) ) {
341
+ continue; // <- Ignore promo alerts.
342
+ }
343
+
344
+ $disable_inputs_needed = ( ! empty( $disable_inputs ) ) ? esc_attr( $disable_inputs ) : '';
345
+ $disabled_tooltip = ( $disabled_message ) ? 'data-tooltip="' . $disabled_message . '"' : '';
346
+ $checkbox_markup = '<input name="alert[]" type="checkbox" class="alert"'. checked( $this->_plugin->alerts->IsEnabled( $alert->code ), true, false ) .' value="'. esc_attr( (int) $alert->code ) .'" '. $disable_inputs_needed .' />';
347
+ $severity = '';
348
+ $severity_obj = $this->_plugin->constants->GetConstantBy( 'value', $alert->severity );
349
+
350
+ if ( is_object( $severity_obj ) ) {
351
+ if ( 'E_CRITICAL' === $severity_obj->name ) {
352
+ $severity = esc_html__( 'Critical', 'wp-security-audit-log' );
353
+ } elseif ( 'E_WARNING' === $severity_obj->name ) {
354
+ $severity = esc_html__( 'Warning', 'wp-security-audit-log' );
355
+ } elseif ( 'E_NOTICE' === $severity_obj->name ) {
356
+ $severity = esc_html__( 'Notification', 'wp-security-audit-log' );
357
+ } elseif ( 'WSAL_CRITICAL' === $severity_obj->name ) {
358
+ $severity = esc_html__( 'Critical', 'wp-security-audit-log' );
359
+ } elseif ( 'WSAL_HIGH' === $severity_obj->name ) {
360
+ $severity = esc_html__( 'High', 'wp-security-audit-log' );
361
+ } elseif ( 'WSAL_MEDIUM' === $severity_obj->name ) {
362
+ $severity = esc_html__( 'Medium', 'wp-security-audit-log' );
363
+ } elseif ( 'WSAL_LOW' === $severity_obj->name ) {
364
+ $severity = esc_html__( 'Low', 'wp-security-audit-log' );
365
+ } elseif ( 'WSAL_INFORMATIONAL' === $severity_obj->name ) {
366
+ $severity = esc_html__( 'Informational', 'wp-security-audit-log' );
367
+ } else {
368
+ $severity = esc_html__( 'Notification', 'wp-security-audit-log' );
369
  }
370
+ }
371
+
372
+ echo '<tr class="alert-wrapper '. $disable_inputs_needed .'" data-alert-cat="' . $alert->catg . '" data-alert-subcat="' . $alert->subcatg . '" ' . $disabled_tooltip . '>';
373
+ echo '<th>' . $checkbox_markup . '</th>';
374
+ echo '<td>' . $alert->code . '</td>';
375
+ echo '<td>' . $severity . '</td>';
376
+ echo '<td>' . $alert->desc . '</td>';
377
+ echo '<td style="display: none;">' . $alert->catg . '</td>';
378
+ echo '<td style="display: none;">' . $alert->subcatg . '</td>';
379
+ echo '</tr>';
380
+
381
+ if ( 4000 === $alert->code ) {
382
+ $frontend_events = WSAL_Settings::get_frontend_events();
383
  ?>
384
+ <tr class="alert-wrapper" data-alert-cat="Users Logins & Sessions Events" data-alert-subcat="User Activity">
385
+ <td></td>
386
+ <td><input type="checkbox" name="frontend-events[register]" id="frontend-events-register" value="1" <?php checked( $frontend_events['register'] ); ?>></td>
387
+ <td colspan="2">
388
+ <label for="frontend-events-register"><?php esc_html_e( 'Keep a log when a visitor registers a user on the website. Only enable this if you allow visitors to register as users on your website. User registration is disabled by default in WordPress.', 'wp-security-audit-log' ); ?></label>
389
+ </td>
390
+ </tr>
391
+ <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  }
 
 
 
 
 
393
 
394
+ if ( 1002 === $alert->code ) {
395
+ $log_failed_login_limit = (int) $this->_plugin->GetGlobalSetting( 'log-failed-login-limit', 10 );
396
+ $log_failed_login_limit = ( -1 === $log_failed_login_limit ) ? '0' : $log_failed_login_limit;
397
+ ?>
398
+ <tr class="alert-wrapper" data-alert-cat="Users Logins & Sessions Events" data-alert-subcat="User Activity">
399
+ <td></td>
400
+ <td><input name="log_failed_login_limit" type="number" class="check_visitor_log" value="<?php echo esc_attr( $log_failed_login_limit ); ?>"></td>
401
+ <td colspan="2">
402
+ <?php esc_html_e( 'Number of login attempts to log. Enter 0 to log all failed login attempts. (By default the plugin only logs up to 10 failed login because the process can be very resource intensive in case of a brute force attack)', 'wp-security-audit-log' ); ?>
403
+ </td>
404
+ </tr>
405
+ <?php
406
+ }
407
+ if ( 1003 === $alert->code ) {
408
+ $log_visitor_failed_login_limit = (int) $this->_plugin->GetGlobalSetting( 'log-visitor-failed-login-limit', 10 );
409
+ $log_visitor_failed_login_limit = ( -1 === $log_visitor_failed_login_limit ) ? '0' : $log_visitor_failed_login_limit;
410
+ ?>
411
+ <tr class="alert-wrapper" data-alert-cat="Users Logins & Sessions Events" data-alert-subcat="User Activity">
412
+ <td></td>
413
+ <td><input name="log_visitor_failed_login_limit" type="number" class="check_visitor_log" value="<?php echo esc_attr( $log_visitor_failed_login_limit ); ?>"></td>
414
+ <td colspan="2">
415
+ <p><?php esc_html_e( 'Number of login attempts to log. Enter 0 to log all failed login attempts. (By default the plugin only logs up to 10 failed login because the process can be very resource intensive in case of a brute force attack)', 'wp-security-audit-log' ); ?></p>
416
+ </td>
417
+ </tr>
418
+ <?php
419
+ }
420
 
421
+ if ( 1000 === $alert->code ) {
422
+ $frontend_events = WSAL_Settings::get_frontend_events();
423
+ ?>
424
+ <tr class="alert-wrapper" data-alert-cat="Users Logins & Sessions Events" data-alert-subcat="User Activity">
425
+ <td></td>
426
+ <td><input type="checkbox" name="frontend-events[login]" id="frontend-events-login" value="1" <?php checked( $frontend_events['login'] ); ?>></td>
427
+ <td colspan="3">
428
+ <label for="frontend-events-login"><?php esc_html_e( 'Keep a log of user log in activity on custom login forms (such as WooCommerce & membership plugins)', 'wp-security-audit-log' ); ?></label>
429
+ </td>
430
+ </tr>
431
+ <?php
432
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
 
434
+ do_action( 'wsal_togglealerts_append_content_to_toggle', $alert->code );
435
+ }
 
 
 
 
 
 
 
 
 
 
436
  }
 
 
437
  }
438
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  ?>
440
  </tbody>
441
  </table>
442
+
443
+ <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php echo esc_attr( __( 'Save Changes', 'wp-security-audit-log' ) ); ?>"></p>
444
+ </form>
445
+
 
 
 
 
 
446
  </div>
447
+ <?php
448
+ $addons = new WSAL_PluginInstallAndActivate();
449
+ $addons->render();
450
+ ?>
451
+ </div>
452
+
453
 
454
  <?php if ( ! empty( $log_level_to_set ) ) : ?>
455
  <!-- Log level updated modal -->
497
  // Remodal styles.
498
  wp_enqueue_style( 'wsal-remodal', WSAL_BASE_URL . 'css/remodal.css', array(), '1.1.1' );
499
  wp_enqueue_style( 'wsal-remodal-theme', WSAL_BASE_URL . 'css/remodal-default-theme.css', array(), '1.1.1' );
500
+
501
+
502
+ // Darktooltip styles.
503
+ wp_enqueue_style(
504
+ 'darktooltip',
505
+ WSAL_BASE_URL . 'css/darktooltip.css',
506
+ array(),
507
+ '0.4.0'
508
+ );
509
+
510
  ?>
511
  <style type="text/css">
512
  .wsal-tab {
585
  }
586
  .addon-wrapper {
587
  flex: 1 0 30%;
588
+ margin: 0 5px 11px 5px;
589
+ border: 1px solid #eee;
590
+ padding: 20px;
591
  }
592
  .addon-wrapper:hover {
593
  border: 1px solid #ccc;
594
  }
595
  .addon-wrapper .button-primary {
596
  position: relative;
597
+ padding: 13px 26px !important;
598
+ font-weight: 500;
599
+ font-size: 16px;
600
+ line-height: 1;
601
+ color: white;
602
+ background: #007cba;
603
+ border: none;
604
+ outline: none;
605
+ overflow: hidden;
606
+ cursor: pointer;
607
+ filter: drop-shadow(0 2px 8px rgba(39, 94, 254, 0.32));
608
+ transition: 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
609
+ line-height: 1 !important;
610
+ }
611
+
612
+ #flitering-options-wrapper {
613
+ display: flex;
614
+ }
615
+ #flitering-options-wrapper .search, #flitering-options-wrapper .choose {
616
+ padding: 10px 0;
617
+ }
618
+ #flitering-options-wrapper > div {
619
+ min-width: 80px;
620
+ }
621
+ .search-title {
622
+ display: flex;
623
+ margin-bottom: 10px;
624
+ }
625
+ .center-bit {
626
+ text-align: center;
627
+ line-height: 82px;
628
+ }
629
+ .center-bit span {
630
+ background: #2271b1;
631
+ color: #fff;
632
+ width: 48px;
633
+ height: 48px;
634
+ border-radius: 50%;
635
+ display: inline-block;
636
+ line-height: 47px;
637
+ margin-top: 20px;
638
+ }
639
+ .search-input-style {
640
+ background-position: 10px 12px;
641
+ background-repeat: no-repeat;
642
+ font-size: 16px;
643
+ margin-bottom: 12px;
644
+ }
645
+ #query-input {
646
+ width: 450px;
647
+ position: relative;
648
+ top: -3px;
649
+ }
650
+ #search-subject {
651
+ width: 190px;
652
+ padding: 2px 8px;
653
+ }
654
+
655
+ #filter-cat {
656
+ width: 400px;
657
+ padding: 2px 8px;
658
+ }
659
+
660
+ #event-toggle-table {
661
+ border-collapse: collapse;
662
+ width: 100%;
663
+ border: 1px solid #ddd;
664
+ font-size: 18px;
665
+ }
666
+
667
+ #event-toggle-table th, #event-toggle-table td {
668
+ text-align: left;
669
+ padding: 12px;
670
+ }
671
+
672
+ #event-toggle-table tr {
673
+ border-bottom: 1px solid #ddd;
674
+ }
675
+
676
+ #event-toggle-table tr.disabled {
677
+ opacity: 0.5;
678
+ }
679
+
680
+ #event-toggle-table tr.header, #event-toggle-table tr:hover {
681
+ background-color: #f1f1f1;
682
+ }
683
+ #tab-all {
684
+ background: #fff;
685
+ margin-top: 10px;
686
+ padding: 10px;
687
+ border: 1px solid #c3c4c7;
688
+ box-shadow: 0 1px 1px rgb(0 0 0 / 4%);
689
  }
690
  </style>
691
  <?php
699
  wp_enqueue_script(
700
  'wsal-remodal-js',
701
  WSAL_BASE_URL . 'js/remodal.min.js',
702
+ array( 'jquery-ui-tooltip' ),
703
  '1.1.1',
704
  true
705
  );
706
+
707
+ // Darktooltip js.
708
+ wp_enqueue_script(
709
+ 'darktooltip', // Identifier.
710
+ WSAL_BASE_URL . 'js/jquery.darktooltip.js', // Script location.
711
+ array( 'jquery' ), // Depends on jQuery.
712
+ '0.4.0', // Script version.
713
+ true
714
+ );
715
+
716
  ?>
717
  <script type="text/javascript">
718
+ function filterEventTable() {
719
+ // Declare variables
720
+ var input, filter, table, tr, td, i, txtValue;
721
+ input = document.getElementById("query-input");
722
+ filter = input.value.toUpperCase();
723
+ table = document.getElementById("event-toggle-table");
724
+ tr = table.getElementsByTagName("tr");
725
+
726
+ var e = document.getElementById("search-subject");
727
+ var strSearch = e.options[e.selectedIndex].value;
728
+
729
+ // Loop through all table rows, and hide those who don't match the search query
730
+ for (i = 0; i < tr.length; i++) {
731
+ if ( strSearch == 'code' ) {
732
+ td = tr[i].getElementsByTagName("td")[0];
733
+ } else if ( strSearch == 'severity' ) {
734
+ td = tr[i].getElementsByTagName("td")[1];
735
+ } else if ( strSearch == 'desc' ) {
736
+ td = tr[i].getElementsByTagName("td")[2];
737
+ }
738
+ if (td) {
739
+ txtValue = td.textContent || td.innerText;
740
+ if (txtValue.toUpperCase().indexOf(filter) > -1) {
741
+ tr[i].style.display = "";
742
+ } else {
743
+ tr[i].style.display = "none";
744
+ }
745
+ }
746
+ }
747
+ }
748
+
749
  jQuery(document).ready(function(){
750
  var scrollHeight = jQuery(document).scrollTop();
751
  // tab handling code
768
  }, 1);
769
  });
770
  // checkbox handling code
771
+ jQuery('#event-toggle-table thead>tr>th>:checkbox').change(function(){
772
+ jQuery(this).parents('table:first').find('tbody>tr:visible>th>:checkbox').attr('checked', this.checked);
773
  });
774
+ jQuery('#event-toggle-table tr checkbox').change(function(){
775
  var allchecked = jQuery(this).parents('tbody:first').find('th>:checkbox:not(:checked)').length === 0;
776
+ jQuery(this).parents('table:first').find('thead>tr:visible>th:first>:checkbox:first').attr('checked', allchecked);
777
  });
778
 
779
  var hashlink = jQuery('#wsal-tabs>a[href="' + location.hash + '"]');
789
  jQuery('#wsal-tabs>a:first').click();
790
  jQuery('.wsal-sub-tabs>a:first').click();
791
  }
792
+
793
+ // Add options to category select box.
794
+ var categories = [];
795
+ jQuery('[data-alert-cat]').each( function( index, value ) {
796
+ var title = jQuery( this ).attr( 'data-alert-cat');
797
+ var found = jQuery.inArray(title, categories);
798
+ if (found >= 0) {
799
+ // Nothing needed.
800
+ } else {
801
+ categories.push( title );
802
+ }
803
+ });
804
+ jQuery( categories ).each( function( index, value ) {
805
+ jQuery('#filter-cat').append('<option value="'+ value +'">'+ value +'</option>');
806
+ });
807
+
808
+ // Display a specific category based on browser hash.
809
+ var hash = window.location.hash;
810
+ jQuery('ul'+hash+':first').show();
811
+
812
+ if (window.location.href.indexOf( '#cat-' ) > -1) {
813
+ var hash = window.location.hash.toUpperCase();
814
+ hash = hash.replace('#CAT-', '');
815
+ jQuery('[data-alert-cat]').each( function( index, value ) {
816
+ var title = jQuery( this ).attr( 'data-alert-cat').toUpperCase();
817
+ if ( title == hash ) {
818
+ jQuery( this ).css( 'display', '' );
819
+ } else {
820
+ jQuery( this ).css( 'display', 'none' );
821
+ }
822
+ });
823
+ }
824
+
825
+ // Filter items based on category.
826
+ jQuery('#filter-cat').on('change', function() {
827
+ var filter = jQuery( "#filter-cat option:selected" ).text().toUpperCase();
828
+ var val = jQuery("#filter-cat").val();
829
+ if ( 'all' == val ) {
830
+ jQuery( '#event-toggle-table tr' ).css( 'display', '' );
831
+ } else {
832
+ jQuery('[data-alert-cat]').each( function( index, value ) {
833
+ var title = jQuery( this ).attr( 'data-alert-cat').toUpperCase();
834
+ if ( title == filter ) {
835
+ jQuery( this ).css( 'display', '' );
836
+ } else {
837
+ jQuery( this ).css( 'display', 'none' );
838
+ }
839
+ });
840
+ }
841
+ });
842
+
843
+ // Lovely tooltop.s
844
+ jQuery( '#event-toggle-table tr' ).darkTooltip( {
845
+ animation: 'fadeIn',
846
+ gravity: 'north',
847
+ size: 'large',
848
+ } );
849
  });
850
 
851
  if (typeof toggle_modal !== 'undefined') {
classes/Views/addons/html-view.php CHANGED
@@ -13,7 +13,9 @@ $utm_params = array(
13
  'utm_source' => 'plugin',
14
  'utm_medium' => 'referral',
15
  'utm_campaign' => 'WSAL',
 
16
  );
 
17
  switch ( $this->hook_suffix ) {
18
  case 'wp-activity-log_page_wsal-loginusers':
19
  $utm_params['utm_content'] = 'sessions';
13
  'utm_source' => 'plugin',
14
  'utm_medium' => 'referral',
15
  'utm_campaign' => 'WSAL',
16
+ 'utm_content' => 'sessions',
17
  );
18
+
19
  switch ( $this->hook_suffix ) {
20
  case 'wp-activity-log_page_wsal-loginusers':
21
  $utm_params['utm_content'] = 'sessions';
css/admin-notices.css CHANGED
@@ -67,6 +67,11 @@ div.wsal_notice__wrapper p {
67
  bottom: 3px;
68
  }
69
 
 
 
 
 
 
70
  /* Addon Notices */
71
  .notice-addon-available {
72
  display: flex;
@@ -90,7 +95,13 @@ div.wsal_notice__wrapper p {
90
  margin-top: 5px;
91
  }
92
 
 
 
 
 
 
 
93
  .updated.wsal_notice {
94
  border: 3px solid #049443;
95
  overflow: hidden;
96
- }
67
  bottom: 3px;
68
  }
69
 
70
+ .wsal_notice__btns a.start-trial-link {
71
+ position: absolute;
72
+ right: 14px;
73
+ bottom: 20px;
74
+ }
75
  /* Addon Notices */
76
  .notice-addon-available {
77
  display: flex;
95
  margin-top: 5px;
96
  }
97
 
98
+ .wsal_notice__btns a.wsal_notice__btn.notice-cta {
99
+ padding: 2px 25px;
100
+ font-weight: bold;
101
+ font-size: 16px;
102
+ }
103
+
104
  .updated.wsal_notice {
105
  border: 3px solid #049443;
106
  overflow: hidden;
107
+ }
defaults.php CHANGED
@@ -1912,6 +1912,22 @@ function wsaldefaults_wsal_init() {
1912
  'user',
1913
  'modified'
1914
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1915
 
1916
  array(
1917
  4025,
@@ -2056,6 +2072,32 @@ function wsaldefaults_wsal_init() {
2056
  'user',
2057
  'created'
2058
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2059
  ),
2060
  ),
2061
 
@@ -2166,7 +2208,7 @@ function wsaldefaults_wsal_init() {
2166
  __( 'Install location', 'wp-security-audit-log' ) => '%install_directory%',
2167
  ],
2168
  [],
2169
- 'wp-activity-log',
2170
  'enabled'
2171
  ),
2172
 
@@ -2179,7 +2221,7 @@ function wsaldefaults_wsal_init() {
2179
  __( 'Install location', 'wp-security-audit-log' ) => '%install_directory%',
2180
  ],
2181
  [],
2182
- 'wp-activity-log',
2183
  'enabled'
2184
  ),
2185
 
@@ -2918,7 +2960,7 @@ function wsaldefaults_wsal_init() {
2918
  6017,
2919
  WSAL_INFORMATIONAL,
2920
  __( 'Modified the list of keywords for comments moderation', 'wp-security-audit-log' ),
2921
- __( 'Modified the list of keywords for comments medoration in WordPress.', 'wp-security-audit-log' ),
2922
  [],
2923
  [],
2924
  'system-setting',
@@ -3057,6 +3099,19 @@ function wsaldefaults_wsal_init() {
3057
  'system-setting',
3058
  'modified'
3059
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
3060
  ),
3061
 
3062
  __( 'Database Events', 'wp-security-audit-log' ) => array(
1912
  'user',
1913
  'modified'
1914
  ),
1915
+ array(
1916
+ 4021,
1917
+ WSAL_MEDIUM,
1918
+ __( 'User\'s website URL was modified', 'wp-security-audit-log' ),
1919
+ __( 'Changed the website URL of the user %TargetUsername% to %new_url%.', 'wp-security-audit-log' ),
1920
+ [
1921
+ __( 'Role', 'wp-security-audit-log' ) => '%Roles%',
1922
+ __( 'First name', 'wp-security-audit-log' ) => '%FirstName%',
1923
+ __( 'Last name', 'wp-security-audit-log' ) => '%LastName%',
1924
+ __( 'Previous website URL', 'wp-security-audit-log' ) => '%old_url%',
1925
+
1926
+ ],
1927
+ wsaldefaults_build_links( [ 'EditUserLink' ] ),
1928
+ 'user',
1929
+ 'modified'
1930
+ ),
1931
 
1932
  array(
1933
  4025,
2072
  'user',
2073
  'created'
2074
  ),
2075
+ array(
2076
+ 4013,
2077
+ WSAL_HIGH,
2078
+ __( 'Network user has been activated', 'wp-security-audit-log' ),
2079
+ __( 'User %NewUserData->Username% has been activated.', 'wp-security-audit-log' ),
2080
+ [
2081
+ __( 'Role', 'wp-security-audit-log' ) => '%NewUserData->Roles%',
2082
+ __( 'First name', 'wp-security-audit-log' ) => '%NewUserData->FirstName%',
2083
+ __( 'Last name', 'wp-security-audit-log' ) => '%NewUserData->LastName%'
2084
+ ],
2085
+ wsaldefaults_build_links( [ 'EditUserLink' ] ),
2086
+ 'user',
2087
+ 'activated'
2088
+ ),
2089
+ array(
2090
+ 4024,
2091
+ WSAL_LOW,
2092
+ __( 'Network user has signed-up', 'wp-security-audit-log' ),
2093
+ __( 'User with the email address %email_address% has signed up to the network.', 'wp-security-audit-log' ),
2094
+ [
2095
+ __( 'Username', 'wp-security-audit-log' ) => '%username%'
2096
+ ],
2097
+ [],
2098
+ 'user',
2099
+ 'created'
2100
+ ),
2101
  ),
2102
  ),
2103
 
2208
  __( 'Install location', 'wp-security-audit-log' ) => '%install_directory%',
2209
  ],
2210
  [],
2211
+ 'plugin',
2212
  'enabled'
2213
  ),
2214
 
2221
  __( 'Install location', 'wp-security-audit-log' ) => '%install_directory%',
2222
  ],
2223
  [],
2224
+ 'theme',
2225
  'enabled'
2226
  ),
2227
 
2960
  6017,
2961
  WSAL_INFORMATIONAL,
2962
  __( 'Modified the list of keywords for comments moderation', 'wp-security-audit-log' ),
2963
+ __( 'Modified the list of keywords for comments moderation in WordPress.', 'wp-security-audit-log' ),
2964
  [],
2965
  [],
2966
  'system-setting',
3099
  'system-setting',
3100
  'modified'
3101
  ),
3102
+
3103
+ array(
3104
+ 6059,
3105
+ WSAL_HIGH,
3106
+ __( 'Option Site Title changed', 'wp-security-audit-log' ),
3107
+ __( 'Changed the <strong>Site Title</strong> to %new_value%.', 'wp-security-audit-log' ),
3108
+ [
3109
+ __( 'Previous setting', 'wp-security-audit-log' ) => '%previous_value%'
3110
+ ],
3111
+ [],
3112
+ 'system-setting',
3113
+ 'modified'
3114
+ ),
3115
  ),
3116
 
3117
  __( 'Database Events', 'wp-security-audit-log' ) => array(
img/addons/wfcm.png ADDED
Binary file
img/help/c4wp.jpg ADDED
Binary file
img/help/password-policy-manager.jpg CHANGED
Binary file
img/help/wp-2fa-img.jpg CHANGED
Binary file
js/auditlog.js CHANGED
@@ -576,4 +576,11 @@ jQuery( document ).ready( function() {
576
  });
577
  });
578
 
 
 
 
 
 
 
 
579
  });
576
  });
577
  });
578
 
579
+ jQuery( '[data-shortened-text]' ).on( 'click', function( event ) {
580
+ var elm = jQuery( this );
581
+ var full_text = elm.data( 'shortened-text' );
582
+ elm.parent().find( 'span' ).text( full_text );
583
+ elm.remove();
584
+ } );
585
+
586
  });
js/common.js CHANGED
@@ -54,59 +54,75 @@ jQuery( document ).ready( function() {
54
  jQuery( 'head' ).append( '<style>.wp-submenu .dashicons-external:before{vertical-align: bottom;}</style>' );
55
 
56
  // Add on installer
57
- jQuery(".install-addon").not('.disabled').click( function(e) {
 
 
 
 
 
 
 
 
 
58
  // Disable other buttons whilst the process is happening.
59
  jQuery(".install-addon").not(this).prop('disabled', true);
60
 
61
- jQuery(this).html( wsalCommonData.installing );
62
- var currentButton = jQuery(this);
63
- var PluginSlug = jQuery(this).attr('data-plugin-slug');
64
- var nonceValue = jQuery(this).attr('data-nonce');
65
- var PluginDownloadUrl = jQuery(this).attr('data-plugin-download-url');
66
- var RedirectToTab = jQuery(this).attr('data-plugin-event-tab-id');
67
- jQuery(currentButton).next('.spinner').show('200');
68
- e.preventDefault();
69
  jQuery.ajax({
70
  type: 'POST',
71
- dataType : "json",
72
  url: wsalCommonData.ajaxURL,
73
- data : {
74
  action: "wsal_run_addon_install",
75
  plugin_slug: PluginSlug,
76
  plugin_url: PluginDownloadUrl,
77
  _wpnonce: nonceValue
78
  },
79
- complete: function( data ) {
80
  var do_redirect = true;
81
- if( data.responseText == '"already_installed"' ) {
82
- jQuery(currentButton).html( wsalCommonData.already_installed ).addClass('disabled');
83
- jQuery(currentButton).next('.spinner').hide('200');
84
- jQuery(currentButton).addClass('disabled');
85
- } else if ( data.responseText == '"activated"' ) {
86
- jQuery(currentButton).html( wsalCommonData.activated ).addClass('disabled');
87
- jQuery(currentButton).next('.spinner').hide('200');
88
- jQuery(currentButton).addClass('disabled');
89
- } else if ( JSON.stringify(data.responseText).toLowerCase().indexOf('failed') >= 0 ) {
90
- jQuery(currentButton).html( wsalCommonData.failed ).addClass('disabled');
91
- jQuery(currentButton).next('.spinner').hide('200');
92
  do_redirect = false;
93
- } else if ( data.responseText == '"success"' || JSON.stringify(data.responseText).toLowerCase().indexOf('success') >= 0 ) {
94
- jQuery(currentButton).html( wsalCommonData.installed ).addClass('disabled');
95
- jQuery(currentButton).next('.spinner').hide('200');
96
  }
97
 
98
- if (do_redirect && typeof RedirectToTab !== 'undefined') {
99
- setTimeout(function(){
100
- window.location="admin.php?page=wsal-togglealerts" + RedirectToTab;
101
- jQuery('[href="' + RedirectToTab + '"]').trigger('click');
102
- // Reload as tabs are not present on page.
103
- window.location.reload();
104
- },100);
 
 
 
 
 
 
 
105
  }
106
- jQuery(".install-addon").not(this).prop('disabled', false);
107
  },
108
  });
109
- });
110
 
111
  // Totally disabling the button.
112
  jQuery(".install-addon.disabled").prop('disabled', true);
54
  jQuery( 'head' ).append( '<style>.wp-submenu .dashicons-external:before{vertical-align: bottom;}</style>' );
55
 
56
  // Add on installer
57
+ jQuery(".install-addon").on( 'click', function( e ) {
58
+ return wsalCommonData.install_addon( e, this );
59
+ });
60
+
61
+ wsalCommonData.install_addon = function( event, button_elm ) {
62
+ var currentButton = jQuery( button_elm );
63
+ if (currentButton.hasClass('disabled')) {
64
+ return;
65
+ }
66
+
67
  // Disable other buttons whilst the process is happening.
68
  jQuery(".install-addon").not(this).prop('disabled', true);
69
 
70
+ currentButton.html(wsalCommonData.installing);
71
+
72
+ var PluginSlug = currentButton.attr('data-plugin-slug');
73
+ var nonceValue = currentButton.attr('data-nonce');
74
+ var PluginDownloadUrl = currentButton.attr('data-plugin-download-url');
75
+ var RedirectToTab = currentButton.attr('data-plugin-event-tab-id');
76
+ currentButton.next('.spinner').show('200');
77
+ event.preventDefault();
78
  jQuery.ajax({
79
  type: 'POST',
80
+ dataType: "json",
81
  url: wsalCommonData.ajaxURL,
82
+ data: {
83
  action: "wsal_run_addon_install",
84
  plugin_slug: PluginSlug,
85
  plugin_url: PluginDownloadUrl,
86
  _wpnonce: nonceValue
87
  },
88
+ complete: function (data) {
89
  var do_redirect = true;
90
+ if (data.responseText == '"already_installed"') {
91
+ currentButton.html(wsalCommonData.already_installed).addClass('disabled');
92
+ currentButton.next('.spinner').hide('200');
93
+ currentButton.addClass('disabled');
94
+ } else if (data.responseText == '"activated"') {
95
+ currentButton.html(wsalCommonData.activated).addClass('disabled');
96
+ currentButton.next('.spinner').hide('200');
97
+ currentButton.addClass('disabled');
98
+ } else if (JSON.stringify(data.responseText).toLowerCase().indexOf('failed') >= 0) {
99
+ currentButton.html(wsalCommonData.failed).addClass('disabled');
100
+ currentButton.next('.spinner').hide('200');
101
  do_redirect = false;
102
+ } else if (data.responseText == '"success"' || JSON.stringify(data.responseText).toLowerCase().indexOf('success') >= 0) {
103
+ currentButton.html(wsalCommonData.installed).addClass('disabled');
104
+ currentButton.next('.spinner').hide('200');
105
  }
106
 
107
+ if ( do_redirect ) {
108
+ if ( typeof RedirectToTab !== 'undefined' ) {
109
+ setTimeout(function () {
110
+ window.location = "admin.php?page=wsal-togglealerts" + RedirectToTab;
111
+ jQuery('[href="' + RedirectToTab + '"]').trigger('click');
112
+ // Reload as tabs are not present on page.
113
+ window.location.reload();
114
+ }, 100);
115
+ } else {
116
+ currentButton.html( wsalCommonData.reloading_page );
117
+ setTimeout(function () {
118
+ window.location.reload();
119
+ }, 100);
120
+ }
121
  }
122
+ jQuery(".install-addon").not(this).prop('disabled', false);
123
  },
124
  });
125
+ };
126
 
127
  // Totally disabling the button.
128
  jQuery(".install-addon.disabled").prop('disabled', true);
languages/index.php ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Nothing to see here.
4
+ */
languages/wp-security-audit-log.pot CHANGED
@@ -27,281 +27,281 @@ msgstr ""
27
  msgid "Exclude custom field from the monitoring"
28
  msgstr ""
29
 
30
- #: classes/AlertFormatter.php:92
31
  msgid "unknown"
32
  msgstr ""
33
 
34
- #: classes/AlertFormatter.php:139
35
  msgid "Download the log file."
36
  msgstr ""
37
 
38
- #: classes/AlertFormatter.php:147
39
  msgid "published"
40
  msgstr ""
41
 
42
- #: classes/AlertManager.php:359
43
  #, php-format
44
  msgid "Event with code %d has not be registered."
45
  msgstr ""
46
 
47
- #: classes/AlertManager.php:443
48
  #, php-format
49
  msgid "Event %s already registered with WP Activity Log."
50
  msgstr ""
51
 
52
- #: classes/AlertManager.php:488
53
  msgid "You have custom events that are using the same ID or IDs which are already registered in the plugin, so they have been disabled."
54
  msgstr ""
55
 
56
- #: classes/AlertManager.php:491
57
  #, php-format
58
  msgid "%4$s to help you solve this issue."
59
  msgstr ""
60
 
61
- #: classes/AlertManager.php:493
62
  msgid "ERROR:"
63
  msgstr ""
64
 
65
- #: classes/AlertManager.php:495
66
  msgid "Contact us"
67
  msgstr ""
68
 
69
- #: classes/AlertManager.php:1069 classes/AuditLogListView.php:272
70
  #: classes/AuditLogListView.php:306 classes/Views/Settings.php:1086
71
  #: classes/WidgetManager.php:76 defaults.php:1706
72
  msgid "User"
73
  msgstr ""
74
 
75
- #: classes/AlertManager.php:1070 classes/AlertManager.php:1760
76
- #: classes/AuditLogGridView.php:436 classes/AuditLogListView.php:442
77
- #: defaults.php:2294
78
  msgid "System"
79
  msgstr ""
80
 
81
- #: classes/AlertManager.php:1071 classes/AuditLogGridView.php:427
82
- #: classes/AuditLogListView.php:436 defaults.php:2138 defaults.php:2153
83
  msgid "Plugin"
84
  msgstr ""
85
 
86
- #: classes/AlertManager.php:1072
87
  msgid "Database"
88
  msgstr ""
89
 
90
- #: classes/AlertManager.php:1073 defaults.php:932
91
  msgid "Post"
92
  msgstr ""
93
 
94
- #: classes/AlertManager.php:1074 classes/AlertManager.php:1078
95
  msgid "File"
96
  msgstr ""
97
 
98
- #: classes/AlertManager.php:1075
99
  msgid "Tag"
100
  msgstr ""
101
 
102
- #: classes/AlertManager.php:1076 defaults.php:79
103
  msgid "Comment"
104
  msgstr ""
105
 
106
- #: classes/AlertManager.php:1077
107
  msgid "Setting"
108
  msgstr ""
109
 
110
- #: classes/AlertManager.php:1079
111
  msgid "System Setting"
112
  msgstr ""
113
 
114
- #: classes/AlertManager.php:1080
115
  msgid "MainWP Network"
116
  msgstr ""
117
 
118
- #: classes/AlertManager.php:1081
119
  msgid "MainWP"
120
  msgstr ""
121
 
122
- #: classes/AlertManager.php:1082
123
  msgid "Category"
124
  msgstr ""
125
 
126
- #: classes/AlertManager.php:1083
127
  msgid "Custom Field"
128
  msgstr ""
129
 
130
- #: classes/AlertManager.php:1084
131
  msgid "Widget"
132
  msgstr ""
133
 
134
- #: classes/AlertManager.php:1085
135
  msgid "Menu"
136
  msgstr ""
137
 
138
- #: classes/AlertManager.php:1086
139
  msgid "Theme"
140
  msgstr ""
141
 
142
- #: classes/AlertManager.php:1087
143
  msgid "Activity log"
144
  msgstr ""
145
 
146
- #: classes/AlertManager.php:1088
147
  msgid "WP Activity Log"
148
  msgstr ""
149
 
150
- #: classes/AlertManager.php:1089
151
  msgid "Multisite Network"
152
  msgstr ""
153
 
154
- #: classes/AlertManager.php:1090
155
  msgid "IP Address"
156
  msgstr ""
157
 
158
- #: classes/AlertManager.php:1106
159
  msgid "unknown object"
160
  msgstr ""
161
 
162
- #: classes/AlertManager.php:1143
163
  msgid "Login"
164
  msgstr ""
165
 
166
- #: classes/AlertManager.php:1144
167
  msgid "Logout"
168
  msgstr ""
169
 
170
- #: classes/AlertManager.php:1145
171
  msgid "Installed"
172
  msgstr ""
173
 
174
- #: classes/AlertManager.php:1146
175
  msgid "Activated"
176
  msgstr ""
177
 
178
- #: classes/AlertManager.php:1147
179
  msgid "Deactivated"
180
  msgstr ""
181
 
182
- #: classes/AlertManager.php:1148
183
  msgid "Uninstalled"
184
  msgstr ""
185
 
186
- #: classes/AlertManager.php:1149
187
  msgid "Updated"
188
  msgstr ""
189
 
190
- #: classes/AlertManager.php:1150
191
  msgid "Created"
192
  msgstr ""
193
 
194
- #: classes/AlertManager.php:1151
195
  msgid "Modified"
196
  msgstr ""
197
 
198
- #: classes/AlertManager.php:1152
199
  msgid "Deleted"
200
  msgstr ""
201
 
202
- #: classes/AlertManager.php:1153
203
  msgid "Published"
204
  msgstr ""
205
 
206
- #: classes/AlertManager.php:1154
207
  msgid "Approved"
208
  msgstr ""
209
 
210
- #: classes/AlertManager.php:1155
211
  msgid "Unapproved"
212
  msgstr ""
213
 
214
- #: classes/AlertManager.php:1156
215
  msgid "Enabled"
216
  msgstr ""
217
 
218
- #: classes/AlertManager.php:1157
219
  msgid "Disabled"
220
  msgstr ""
221
 
222
- #: classes/AlertManager.php:1158
223
  msgid "Added"
224
  msgstr ""
225
 
226
- #: classes/AlertManager.php:1159
227
  msgid "Failed Login"
228
  msgstr ""
229
 
230
- #: classes/AlertManager.php:1160
231
  msgid "Blocked"
232
  msgstr ""
233
 
234
- #: classes/AlertManager.php:1161
235
  msgid "Uploaded"
236
  msgstr ""
237
 
238
- #: classes/AlertManager.php:1162
239
  msgid "Restored"
240
  msgstr ""
241
 
242
- #: classes/AlertManager.php:1163
243
  msgid "Opened"
244
  msgstr ""
245
 
246
- #: classes/AlertManager.php:1164
247
  msgid "Viewed"
248
  msgstr ""
249
 
250
- #: classes/AlertManager.php:1165
251
  msgid "Started"
252
  msgstr ""
253
 
254
- #: classes/AlertManager.php:1166
255
  msgid "Stopped"
256
  msgstr ""
257
 
258
- #: classes/AlertManager.php:1167
259
  msgid "Removed"
260
  msgstr ""
261
 
262
- #: classes/AlertManager.php:1168
263
  msgid "Unblocked"
264
  msgstr ""
265
 
266
- #: classes/AlertManager.php:1169
267
  msgid "Renamed"
268
  msgstr ""
269
 
270
- #: classes/AlertManager.php:1170
271
  msgid "Duplicated"
272
  msgstr ""
273
 
274
- #: classes/AlertManager.php:1171
275
  msgid "Submitted"
276
  msgstr ""
277
 
278
- #: classes/AlertManager.php:1172
279
  msgid "Revoked"
280
  msgstr ""
281
 
282
- #: classes/AlertManager.php:1189
283
  msgid "unknown type"
284
  msgstr ""
285
 
286
- #: classes/AlertManager.php:1437 classes/Views/ToggleAlerts.php:211
287
- #: classes/Views/ToggleAlerts.php:244 defaults.php:1508
288
  msgid "Pages"
289
  msgstr ""
290
 
291
- #: classes/AlertManager.php:1437 classes/Views/ToggleAlerts.php:211
292
- #: classes/Views/ToggleAlerts.php:244 defaults.php:1323
293
  msgid "Custom Post Types"
294
  msgstr ""
295
 
296
- #: classes/AlertManager.php:1658
297
  msgid "System Activity"
298
  msgstr ""
299
 
300
- #: classes/AlertManager.php:1722 classes/ConstantManager.php:147
301
  msgid "Unknown error code."
302
  msgstr ""
303
 
304
- #: classes/AlertManager.php:1729 classes/AlertManager.php:1741
305
  msgid "Unknown Site"
306
  msgstr ""
307
 
@@ -330,7 +330,7 @@ msgid "— End of Activity Log —"
330
  msgstr ""
331
 
332
  #: classes/AuditLogGridView.php:211 classes/AuditLogListView.php:209
333
- #: classes/Views/AuditLog.php:589
334
  msgid "All Sites"
335
  msgstr ""
336
 
@@ -342,7 +342,8 @@ msgstr ""
342
 
343
  #: classes/AuditLogGridView.php:272 classes/AuditLogGridView.php:298
344
  #: classes/AuditLogListView.php:270 classes/AuditLogListView.php:300
345
- #: classes/Views/Settings.php:1082 classes/Views/ToggleAlerts.php:299
 
346
  msgid "Severity"
347
  msgstr ""
348
 
@@ -373,57 +374,57 @@ msgid "Message:"
373
  msgstr ""
374
 
375
  #: classes/AuditLogGridView.php:401 classes/AuditLogGridView.php:405
376
- #: classes/AuditLogListView.php:413 classes/Utilities/UserUtils.php:149
377
  msgid "Unknown"
378
  msgstr ""
379
 
380
- #: classes/AuditLogGridView.php:430 classes/AuditLogListView.php:438
381
- #: defaults.php:2063
382
  msgid "Plugins"
383
  msgstr ""
384
 
385
- #: classes/AuditLogGridView.php:433 classes/AuditLogListView.php:440
386
  msgid "Unregistered user"
387
  msgstr ""
388
 
389
- #: classes/AuditLogGridView.php:470 classes/AuditLogGridView.php:483
390
  #: classes/AuditLogListView.php:473 classes/AuditLogListView.php:486
391
  msgid "Show me all activity originating from this IP Address"
392
  msgstr ""
393
 
394
- #: classes/AuditLogGridView.php:512
395
  msgid "Date:"
396
  msgstr ""
397
 
398
- #: classes/AuditLogGridView.php:516
399
  msgid "Time:"
400
  msgstr ""
401
 
402
- #: classes/AuditLogGridView.php:520
403
  msgid "User:"
404
  msgstr ""
405
 
406
- #: classes/AuditLogGridView.php:524
407
  msgid "IP:"
408
  msgstr ""
409
 
410
- #: classes/AuditLogGridView.php:528
411
  msgid "Object:"
412
  msgstr ""
413
 
414
- #: classes/AuditLogGridView.php:532
415
  msgid "Event Type:"
416
  msgstr ""
417
 
418
- #: classes/AuditLogGridView.php:540 classes/AuditLogListView.php:518
419
  msgid "View all details of this change"
420
  msgstr ""
421
 
422
- #: classes/AuditLogGridView.php:541 classes/AuditLogListView.php:519
423
  msgid "Alert Data Inspector"
424
  msgstr ""
425
 
426
- #: classes/AuditLogGridView.php:701 classes/AuditLogListView.php:683
427
  msgid "Select All"
428
  msgstr ""
429
 
@@ -445,11 +446,11 @@ msgstr ""
445
  msgid "Event Type"
446
  msgstr ""
447
 
448
- #: classes/AuditLogListView.php:382 classes/Models/Occurrence.php:90
449
  msgid "Alert message not found."
450
  msgstr ""
451
 
452
- #: classes/AuditLogListView.php:383 classes/Models/Occurrence.php:91
453
  msgid "Alert description not found."
454
  msgstr ""
455
 
@@ -463,89 +464,85 @@ msgid "Code %1$d: %2$s"
463
  msgstr ""
464
 
465
  #: classes/ConstantManager.php:159 classes/ConstantManager.php:165
466
- #: classes/Views/ToggleAlerts.php:430 classes/Views/ToggleAlerts.php:436
467
  msgid "Critical"
468
  msgstr ""
469
 
470
- #: classes/ConstantManager.php:161 classes/Views/ToggleAlerts.php:432
471
  msgid "Warning"
472
  msgstr ""
473
 
474
- #: classes/ConstantManager.php:163 classes/Views/ToggleAlerts.php:434
475
- #: classes/Views/ToggleAlerts.php:446
476
  msgid "Notification"
477
  msgstr ""
478
 
479
- #: classes/ConstantManager.php:167 classes/Views/ToggleAlerts.php:438
480
  msgid "High"
481
  msgstr ""
482
 
483
- #: classes/ConstantManager.php:169 classes/Views/ToggleAlerts.php:440
484
  msgid "Medium"
485
  msgstr ""
486
 
487
- #: classes/ConstantManager.php:171 classes/Views/ToggleAlerts.php:442
488
  msgid "Low"
489
  msgstr ""
490
 
491
- #: classes/ConstantManager.php:173 classes/Views/ToggleAlerts.php:444
492
  msgid "Informational"
493
  msgstr ""
494
 
495
- #: classes/Models/Occurrence.php:206
496
- msgid "WFCM"
497
- msgstr ""
498
-
499
- #: classes/Models/Occurrence.php:231
500
  #, php-format
501
  msgid "This type of activity / change is no longer monitored. You can create your own custom event IDs to keep a log of such change. Read more about custom events %1$shere%2$s."
502
  msgstr ""
503
 
504
- #: classes/Sensors/Content.php:735
505
  msgid "Default template"
506
  msgstr ""
507
 
508
- #: classes/Sensors/Content.php:736
509
  msgid "Default"
510
  msgstr ""
511
 
512
- #: classes/Sensors/Content.php:778
513
  msgid "No previous image"
514
  msgstr ""
515
 
516
- #: classes/Sensors/Content.php:779
517
  msgid "No image"
518
  msgstr ""
519
 
520
- #: classes/Sensors/Content.php:1133 classes/Sensors/Content.php:1141
521
  msgid "Password Protected"
522
  msgstr ""
523
 
524
- #: classes/Sensors/Content.php:1135 classes/Sensors/Content.php:1143
525
  msgid "Private"
526
  msgstr ""
527
 
528
- #: classes/Sensors/Content.php:1137 classes/Sensors/Content.php:1145
529
  msgid "Public"
530
  msgstr ""
531
 
532
- #: classes/Sensors/Content.php:1308
533
  msgid "no tags"
534
  msgstr ""
535
 
536
- #: classes/Sensors/Multisite.php:80
537
  msgid "disabled"
538
  msgstr ""
539
 
540
- #: classes/Sensors/Multisite.php:81
541
  msgid "user accounts only"
542
  msgstr ""
543
 
544
- #: classes/Sensors/Multisite.php:82
545
  msgid "users can register new sites"
546
  msgstr ""
547
 
548
- #: classes/Sensors/Multisite.php:83
549
  msgid "sites & users can be registered"
550
  msgstr ""
551
 
@@ -569,39 +566,39 @@ msgstr ""
569
  msgid "This function is deprecated"
570
  msgstr ""
571
 
572
- #: classes/Settings.php:1914
573
  msgid "Root directory of WordPress (excluding sub directories)"
574
  msgstr ""
575
 
576
- #: classes/Settings.php:1915
577
  msgid "WP Admin directory (/wp-admin/)"
578
  msgstr ""
579
 
580
- #: classes/Settings.php:1916
581
  msgid "WP Includes directory (/wp-includes/)"
582
  msgstr ""
583
 
584
- #: classes/Settings.php:1917
585
  msgid "/wp-content/ directory (excluding plugins, themes & uploads directories)"
586
  msgstr ""
587
 
588
- #: classes/Settings.php:1918
589
  msgid "Themes directory (/wp-content/themes/)"
590
  msgstr ""
591
 
592
- #: classes/Settings.php:1919
593
  msgid "Plugins directory (/wp-content/plugins/)"
594
  msgstr ""
595
 
596
- #: classes/Settings.php:1920
597
  msgid "Uploads directory (/wp-content/uploads/)"
598
  msgstr ""
599
 
600
- #: classes/Settings.php:1925
601
  msgid "Uploads directory of all sub sites on this network (/wp-content/sites/*)"
602
  msgstr ""
603
 
604
- #: classes/Settings.php:2079
605
  msgid "None provided"
606
  msgstr ""
607
 
@@ -609,6 +606,14 @@ msgstr ""
609
  msgid "Keep a record of when someone adds, modifies or deletes forms, entries and more in the Gravity Forms plugin."
610
  msgstr ""
611
 
 
 
 
 
 
 
 
 
612
  #: classes/Utilities/Emailer.php:54
613
  #, php-format
614
  msgid "WP Activity Log plugin disabled on %s"
@@ -628,83 +633,83 @@ msgid "WP Activity Log can keep a log of changes done on other plugins. Install
628
  msgstr ""
629
 
630
  #: classes/Utilities/PluginInstallAndActivate.php:101
631
- #: classes/Views/SetupWizard.php:834
632
  msgid "Extension for "
633
  msgstr ""
634
 
635
  #: classes/Utilities/PluginInstallAndActivate.php:106
636
- #: classes/Views/SetupWizard.php:839
637
  msgid "Extension installed, activate now?"
638
  msgstr ""
639
 
640
  #: classes/Utilities/PluginInstallAndActivate.php:108
641
- #: classes/Views/SetupWizard.php:272 classes/Views/SetupWizard.php:841
642
- #: wp-security-audit-log.php:1309
643
  msgid "Extension installed"
644
  msgstr ""
645
 
646
  #: classes/Utilities/PluginInstallAndActivate.php:110
647
- #: classes/Views/SetupWizard.php:843
648
  msgid "Install Extension"
649
  msgstr ""
650
 
651
- #: classes/Utilities/PluginInstallerAction.php:80
652
  msgid "Tried to install a zip or slug that was not in the allowed list"
653
  msgstr ""
654
 
655
- #: classes/Utilities/UserUtils.php:91
656
  msgid "Username: "
657
  msgstr ""
658
 
659
- #: classes/Utilities/UserUtils.php:92
660
  msgid "First name: "
661
  msgstr ""
662
 
663
- #: classes/Utilities/UserUtils.php:93
664
  msgid "Last Name: "
665
  msgstr ""
666
 
667
- #: classes/Utilities/UserUtils.php:94
668
  msgid "Email: "
669
  msgstr ""
670
 
671
- #: classes/Utilities/UserUtils.php:95
672
  msgid "Nickname: "
673
  msgstr ""
674
 
675
- #: classes/ViewManager.php:142
676
  msgid "WP Activity Log requires Website File Changes Monitor 1.6.0. Please upgrade that plugin."
677
  msgstr ""
678
 
679
- #: classes/ViewManager.php:284
680
  msgid "Free Premium Trial"
681
  msgstr ""
682
 
683
- #: classes/Views/AuditLog.php:98
684
  msgid "Get email notifications about website changes, view logged-in users, do granular log searches, create detailed reports, and more."
685
  msgstr ""
686
 
687
- #: classes/Views/AuditLog.php:99
688
  msgid "Upgrade to premium today and get more out of your activity logs!"
689
  msgstr ""
690
 
691
- #: classes/Views/AuditLog.php:102
692
  msgid "Instant SMS & email alerts, search & filters, reports, users sessions management and much more!"
693
  msgstr ""
694
 
695
- #: classes/Views/AuditLog.php:103
696
  msgid "Upgrade to premium to get more out of your activity logs!"
697
  msgstr ""
698
 
699
- #: classes/Views/AuditLog.php:106
700
  msgid "See who logged in on your site in real-time, generate reports, get SMS & email alerts of critical changes and more!"
701
  msgstr ""
702
 
703
- #: classes/Views/AuditLog.php:107
704
  msgid "Unlock these and other powerful features with WP Activity Log Premium."
705
  msgstr ""
706
 
707
- #: classes/Views/AuditLog.php:158
708
  msgid "Learn more"
709
  msgstr ""
710
 
@@ -744,106 +749,106 @@ msgstr ""
744
  msgid "No, thank you"
745
  msgstr ""
746
 
747
- #: classes/Views/AuditLog.php:272
748
  msgid "We noticed you have"
749
  msgstr ""
750
 
751
- #: classes/Views/AuditLog.php:274
752
  msgid "installed."
753
  msgstr ""
754
 
755
- #: classes/Views/AuditLog.php:276
756
  msgid "Install extension"
757
  msgstr ""
758
 
759
- #: classes/Views/AuditLog.php:321
760
  msgid "Activity Log Viewer"
761
  msgstr ""
762
 
763
- #: classes/Views/AuditLog.php:348
764
  msgid "Log Viewer"
765
  msgstr ""
766
 
767
- #: classes/Views/AuditLog.php:505 classes/Views/Settings.php:324
768
- #: classes/Views/ToggleAlerts.php:109
769
  msgid "You do not have sufficient permissions to access this page."
770
  msgstr ""
771
 
772
- #: classes/Views/AuditLog.php:554
773
  msgid "Thank you for installing WP Activity Log. Do you want to run the wizard to configure the basic plugin settings?"
774
  msgstr ""
775
 
776
- #: classes/Views/AuditLog.php:556 classes/Views/Settings.php:532
777
  #: classes/Views/Settings.php:559 classes/Views/Settings.php:625
778
  #: classes/Views/Settings.php:683 classes/Views/Settings.php:1118
779
- #: classes/Views/Settings.php:1402 classes/Views/Settings.php:1443
780
- #: classes/Views/Settings.php:1464 classes/Views/Settings.php:1474
781
- #: classes/Views/SetupWizard.php:562
782
  msgid "Yes"
783
  msgstr ""
784
 
785
- #: classes/Views/AuditLog.php:557 classes/Views/Settings.php:537
786
  #: classes/Views/Settings.php:564 classes/Views/Settings.php:655
787
  #: classes/Views/Settings.php:693 classes/Views/Settings.php:1123
788
- #: classes/Views/Settings.php:1409 classes/Views/Settings.php:1450
789
- #: classes/Views/Settings.php:1465 classes/Views/Settings.php:1475
790
- #: classes/Views/SetupWizard.php:567
791
  msgid "No"
792
  msgstr ""
793
 
794
- #: classes/Views/AuditLog.php:588
795
  msgid "Please enter the number of alerts you would like to see on one page:"
796
  msgstr ""
797
 
798
- #: classes/Views/AuditLog.php:590
799
  msgid "No Results"
800
  msgstr ""
801
 
802
- #: classes/Views/AuditLog.php:727 classes/Views/AuditLog.php:770
803
- #: classes/Views/AuditLog.php:1048 classes/Views/AuditLog.php:1110
804
- #: classes/Views/AuditLog.php:1163 classes/Views/Settings.php:239
805
- #: classes/Views/Settings.php:1759 classes/Views/SetupWizard.php:96
806
  msgid "Nonce verification failed."
807
  msgstr ""
808
 
809
- #: classes/Views/AuditLog.php:745
810
  msgid "No users found."
811
  msgstr ""
812
 
813
- #: classes/Views/AuditLog.php:810
814
  msgid "Freemius opt choice selected."
815
  msgstr ""
816
 
817
- #: classes/Views/AuditLog.php:817
818
  msgid "Freemius opt choice not found."
819
  msgstr ""
820
 
821
- #: classes/Views/AuditLog.php:913
822
  #, php-format
823
  msgid "<br>An error occurred when trying to install and activate the plugin. Please try install it again from the %1$sevent settings%2$s page."
824
  msgstr ""
825
 
826
- #: classes/Views/AuditLog.php:1012
827
  msgid "WordPress Activity Log"
828
  msgstr ""
829
 
830
- #: classes/Views/AuditLog.php:1013
831
  msgid "When a user makes a change on your website the plugin will keep a record of that event here. Right now there is nothing because this is a new install."
832
  msgstr ""
833
 
834
- #: classes/Views/AuditLog.php:1014
835
  msgid "Thank you for using WP Activity Log"
836
  msgstr ""
837
 
838
- #: classes/Views/AuditLog.php:1037 classes/Views/AuditLog.php:1147
839
  msgid "You do not have sufficient permissions to dismiss this notice."
840
  msgstr ""
841
 
842
- #: classes/Views/AuditLog.php:1105
843
  msgid "Access Denied"
844
  msgstr ""
845
 
846
- #: classes/Views/AuditLog.php:1200
847
  #, php-format
848
  msgid "Install the activity log extension for %1$s for more detailed logging of changes done in %2$s."
849
  msgstr ""
@@ -853,7 +858,7 @@ msgid "Notifications Extension"
853
  msgstr ""
854
 
855
  #: classes/Views/EmailNotifications.php:35
856
- msgid "Notifications &#8682;"
857
  msgstr ""
858
 
859
  #: classes/Views/EmailNotifications.php:49
@@ -1049,26 +1054,30 @@ msgid "Rate Plugin"
1049
  msgstr ""
1050
 
1051
  #: classes/Views/Help.php:282
1052
- msgid "Enforce strong password policies on WordPress"
1053
  msgstr ""
1054
 
1055
  #: classes/Views/Help.php:288
1056
- msgid "Automatically identify unauthorized file changes on WordPress"
1057
  msgstr ""
1058
 
1059
  #: classes/Views/Help.php:294
1060
- msgid "Add an extra layer of security to your login pages with 2FA & require your users to use it."
1061
  msgstr ""
1062
 
1063
  #: classes/Views/Help.php:300
1064
- msgid "See the child sites activity logs from the central MainWP dashboard"
1065
  msgstr ""
1066
 
1067
  #: classes/Views/Help.php:306
 
 
 
 
1068
  msgid "Our other WordPress plugins"
1069
  msgstr ""
1070
 
1071
- #: classes/Views/Help.php:317
1072
  msgid "LEARN MORE"
1073
  msgstr ""
1074
 
@@ -1125,7 +1134,7 @@ msgid "Reports Extension"
1125
  msgstr ""
1126
 
1127
  #: classes/Views/Reports.php:35
1128
- msgid "Reports &#8682;"
1129
  msgstr ""
1130
 
1131
  #: classes/Views/Reports.php:49
@@ -1173,7 +1182,7 @@ msgid "Search Extension"
1173
  msgstr ""
1174
 
1175
  #: classes/Views/Search.php:35
1176
- msgid "Search &#8682;"
1177
  msgstr ""
1178
 
1179
  #: classes/Views/Search.php:49
@@ -1248,8 +1257,8 @@ msgstr ""
1248
  msgid "Unknown settings tab."
1249
  msgstr ""
1250
 
1251
- #: classes/Views/Settings.php:224 classes/Views/Settings.php:1769
1252
- #: classes/Views/Settings.php:1792 classes/Views/SetupWizard.php:83
1253
  msgid "Access Denied."
1254
  msgstr ""
1255
 
@@ -1261,11 +1270,11 @@ msgstr ""
1261
  msgid "Message sent successfully."
1262
  msgstr ""
1263
 
1264
- #: classes/Views/Settings.php:340 classes/Views/ToggleAlerts.php:126
1265
  msgid "Settings have been saved."
1266
  msgstr ""
1267
 
1268
- #: classes/Views/Settings.php:346 classes/Views/ToggleAlerts.php:132
1269
  msgid "Error: "
1270
  msgstr ""
1271
 
@@ -1483,7 +1492,7 @@ msgstr ""
1483
  #: classes/Views/Settings.php:834 classes/Views/Settings.php:1217
1484
  #: classes/Views/Settings.php:1238 classes/Views/Settings.php:1259
1485
  #: classes/Views/Settings.php:1281 classes/Views/Settings.php:1329
1486
- #: classes/Views/Settings.php:1583
1487
  msgid "Remove"
1488
  msgstr ""
1489
 
@@ -1639,19 +1648,19 @@ msgstr ""
1639
  msgid "Enable Events for WordPress Background Activity"
1640
  msgstr ""
1641
 
1642
- #: classes/Views/Settings.php:1172 classes/Views/ToggleAlerts.php:522
1643
  msgid "Website File Changes Monitor"
1644
  msgstr ""
1645
 
1646
- #: classes/Views/Settings.php:1173 classes/Views/ToggleAlerts.php:523
1647
  msgid "To keep a log of file changes please install Website File Changes Monitor, a plugin which is also developed by us."
1648
  msgstr ""
1649
 
1650
- #: classes/Views/Settings.php:1174 classes/Views/ToggleAlerts.php:524
1651
  msgid "Install plugin now"
1652
  msgstr ""
1653
 
1654
- #: classes/Views/Settings.php:1174 classes/Views/ToggleAlerts.php:524
1655
  msgid "Learn More"
1656
  msgstr ""
1657
 
@@ -1755,119 +1764,119 @@ msgstr ""
1755
  msgid "Enable MainWP Child Site Stealth Mode"
1756
  msgstr ""
1757
 
1758
- #: classes/Views/Settings.php:1417
1759
  msgid "Admin blocking plugins support"
1760
  msgstr ""
1761
 
1762
- #: classes/Views/Settings.php:1422
1763
  msgid "Enable early plugin loading on sites that use admin blocking plugins"
1764
  msgstr ""
1765
 
1766
- #: classes/Views/Settings.php:1431
1767
  msgid "Do you want to delete the plugin data from the database upon uninstall?"
1768
  msgstr ""
1769
 
1770
- #: classes/Views/Settings.php:1432
1771
  msgid "The plugin saves the activity log data and settings in the WordPress database. By default upon uninstalling the plugin the data is kept in the database so if it is installed again, you can still access the data. If the data is deleted it is not possible to recover it so you won't be able to access it again even when you reinstall the plugin."
1772
  msgstr ""
1773
 
1774
- #: classes/Views/Settings.php:1436
1775
  msgid "Remove Data on Uninstall"
1776
  msgstr ""
1777
 
1778
- #: classes/Views/Settings.php:1461
1779
  msgid "Are you sure you want to reset all the plugin settings to default? This action cannot be undone."
1780
  msgstr ""
1781
 
1782
- #: classes/Views/Settings.php:1471
1783
  msgid "Are you sure you want to purge all the activity log data?"
1784
  msgstr ""
1785
 
1786
- #: classes/Views/Settings.php:1495
1787
  msgid "MainWP Child plugin is not active on this website."
1788
  msgstr ""
1789
 
1790
- #: classes/Views/Settings.php:1576
1791
  msgid "The specified value is not a valid URL!"
1792
  msgstr ""
1793
 
1794
- #: classes/Views/Settings.php:1577
1795
  msgid "The specified value is not a valid post type!"
1796
  msgstr ""
1797
 
1798
- #: classes/Views/Settings.php:1578
1799
  msgid "The specified value is not a valid IP address!"
1800
  msgstr ""
1801
 
1802
- #: classes/Views/Settings.php:1579
1803
  msgid "The specified value is not a user nor a role!"
1804
  msgstr ""
1805
 
1806
- #: classes/Views/Settings.php:1580
1807
  msgid "Filename cannot be added because it contains invalid characters."
1808
  msgstr ""
1809
 
1810
- #: classes/Views/Settings.php:1581
1811
  msgid "File extension cannot be added because it contains invalid characters."
1812
  msgstr ""
1813
 
1814
- #: classes/Views/Settings.php:1582
1815
  msgid "Directory cannot be added because it contains invalid characters."
1816
  msgstr ""
1817
 
1818
- #: classes/Views/Settings.php:1584
1819
  msgid "Please save any changes before switching tabs."
1820
  msgstr ""
1821
 
1822
- #: classes/Views/Settings.php:1775 classes/Views/Settings.php:1798
1823
  msgid "Nonce Verification Failed."
1824
  msgstr ""
1825
 
1826
- #: classes/Views/Settings.php:1783
1827
  msgid "Plugin settings have been reset."
1828
  msgstr ""
1829
 
1830
- #: classes/Views/Settings.php:1807
1831
  msgid "Tables has been reset."
1832
  msgstr ""
1833
 
1834
- #: classes/Views/Settings.php:1809
1835
  msgid "Reset query failed."
1836
  msgstr ""
1837
 
1838
- #: classes/Views/Settings.php:1822
1839
  msgid "Activity log retention"
1840
  msgstr ""
1841
 
1842
- #: classes/Views/Settings.php:1828
1843
  msgid "Keep all data"
1844
  msgstr ""
1845
 
1846
- #: classes/Views/Settings.php:1844
1847
  msgid "Days"
1848
  msgstr ""
1849
 
1850
- #: classes/Views/Settings.php:1845
1851
  msgid "Months"
1852
  msgstr ""
1853
 
1854
- #: classes/Views/Settings.php:1846
1855
  msgid "Years"
1856
  msgstr ""
1857
 
1858
- #: classes/Views/Settings.php:1861
1859
  msgid "Delete events older than"
1860
  msgstr ""
1861
 
1862
- #: classes/Views/Settings.php:1879
1863
  msgid "The next scheduled purging of activity log data that is older than "
1864
  msgstr ""
1865
 
1866
- #: classes/Views/Settings.php:1886
1867
  msgid "You can run the purging process now by clicking the button below."
1868
  msgstr ""
1869
 
1870
- #: classes/Views/Settings.php:1890
1871
  msgid "Purge Old Data"
1872
  msgstr ""
1873
 
@@ -1891,8 +1900,8 @@ msgstr ""
1891
  msgid "Log Retention"
1892
  msgstr ""
1893
 
1894
- #: classes/Views/SetupWizard.php:202 classes/Views/SetupWizard.php:741
1895
- #: classes/Views/SetupWizard.php:742
1896
  msgid "Finish"
1897
  msgstr ""
1898
 
@@ -1908,156 +1917,160 @@ msgstr ""
1908
  msgid "Specified value in not an IP address."
1909
  msgstr ""
1910
 
1911
- #: classes/Views/SetupWizard.php:270 wp-security-audit-log.php:1307
1912
  msgid "Installing, please wait"
1913
  msgstr ""
1914
 
1915
- #: classes/Views/SetupWizard.php:271 wp-security-audit-log.php:1308
1916
  msgid "Already installed"
1917
  msgstr ""
1918
 
1919
- #: classes/Views/SetupWizard.php:273 wp-security-audit-log.php:1310
1920
  msgid "Extension activated"
1921
  msgstr ""
1922
 
1923
- #: classes/Views/SetupWizard.php:274 wp-security-audit-log.php:1311
1924
  msgid "Install failed"
1925
  msgstr ""
1926
 
1927
- #: classes/Views/SetupWizard.php:304
 
 
 
 
1928
  msgid "WP Activity Log &rsaquo; Setup Wizard"
1929
  msgstr ""
1930
 
1931
- #: classes/Views/SetupWizard.php:322
1932
  msgid "Close Wizard"
1933
  msgstr ""
1934
 
1935
- #: classes/Views/SetupWizard.php:415
1936
  #, php-format
1937
  msgid "You have reached an invaild step - %1$sreturn to the start of the wizard%2$s."
1938
  msgstr ""
1939
 
1940
- #: classes/Views/SetupWizard.php:432
1941
  msgid "This wizard helps you configure the basic plugin settings. All these settings can be changed at a later stage from the plugin settings."
1942
  msgstr ""
1943
 
1944
- #: classes/Views/SetupWizard.php:437
1945
  msgid "Start Configuring the Plugin"
1946
  msgstr ""
1947
 
1948
- #: classes/Views/SetupWizard.php:441
1949
  msgid "Exit Wizard"
1950
  msgstr ""
1951
 
1952
- #: classes/Views/SetupWizard.php:454
1953
  msgid "Please select the level of detail for your WordPress activity logs:"
1954
  msgstr ""
1955
 
1956
- #: classes/Views/SetupWizard.php:458
1957
  msgid "Basic (I want a high level overview and I am not interested in the detail)"
1958
  msgstr ""
1959
 
1960
- #: classes/Views/SetupWizard.php:463
1961
  msgid "Geek (I want to know everything that is happening on my WordPress)"
1962
  msgstr ""
1963
 
1964
- #: classes/Views/SetupWizard.php:465
1965
  msgid "Note: You can change the WordPress logging level from the plugin’s settings anytime."
1966
  msgstr ""
1967
 
1968
- #: classes/Views/SetupWizard.php:468 classes/Views/SetupWizard.php:525
1969
- #: classes/Views/SetupWizard.php:574 classes/Views/SetupWizard.php:634
1970
- #: classes/Views/SetupWizard.php:635 classes/Views/SetupWizard.php:856
1971
- #: classes/Views/SetupWizard.php:857
1972
  msgid "Next"
1973
  msgstr ""
1974
 
1975
- #: classes/Views/SetupWizard.php:509
1976
  msgid "Do you or your users use other pages to log in to WordPress other than the default login page ( /wp-admin/ )?"
1977
  msgstr ""
1978
 
1979
- #: classes/Views/SetupWizard.php:513
1980
  msgid "Yes, we use other pages to login to WordPress."
1981
  msgstr ""
1982
 
1983
- #: classes/Views/SetupWizard.php:518
1984
  msgid "No, we only use the default WordPress login page."
1985
  msgstr ""
1986
 
1987
- #: classes/Views/SetupWizard.php:520
1988
  msgid "If your website is a membership or ecommerce website most probably you have more than one area from where the users can login. If you are not sure, select Yes."
1989
  msgstr ""
1990
 
1991
- #: classes/Views/SetupWizard.php:523 classes/Views/SetupWizard.php:572
1992
- #: classes/Views/SetupWizard.php:626
1993
  msgid "Note: You can change the WordPress activity log retention settings at any time from the plugin settings later on."
1994
  msgstr ""
1995
 
1996
- #: classes/Views/SetupWizard.php:558
1997
  msgid "Can visitors register as a user on your website?"
1998
  msgstr ""
1999
 
2000
- #: classes/Views/SetupWizard.php:569
2001
  msgid "If you are not sure about this setting, check if the Membership setting in the WordPress General settings is checked or not. If it is not checked (default) select No."
2002
  msgstr ""
2003
 
2004
- #: classes/Views/SetupWizard.php:608
2005
  msgid "How long do you want to keep the data in the WordPress activity Log?"
2006
  msgstr ""
2007
 
2008
- #: classes/Views/SetupWizard.php:613
2009
  msgid "6 months (data older than 6 months will be deleted)"
2010
  msgstr ""
2011
 
2012
- #: classes/Views/SetupWizard.php:618
2013
  msgid "12 months (data older than 12 months will be deleted)"
2014
  msgstr ""
2015
 
2016
- #: classes/Views/SetupWizard.php:623
2017
  msgid "Keep all data."
2018
  msgstr ""
2019
 
2020
- #: classes/Views/SetupWizard.php:644
2021
  msgid "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://wpactivitylog.com/features/?utm_source=plugin&utm_medium=referral&utm_campaign=WSAL&utm_content=wizard+configuration\" target=\"_blank\">upgrade to Premium</a> and use the Database tools to store the WordPress activity log in an external database."
2022
  msgstr ""
2023
 
2024
- #: classes/Views/SetupWizard.php:707
2025
  msgid "Your plugin is all set and it is ready to start keeping a record of everything that is happening on your WordPress in a WordPress activity log."
2026
  msgstr ""
2027
 
2028
- #: classes/Views/SetupWizard.php:708
2029
  msgid "Below are a few useful links you might need to refer to:"
2030
  msgstr ""
2031
 
2032
- #: classes/Views/SetupWizard.php:713
2033
  msgid "Getting started with the WP Activity Log plugin"
2034
  msgstr ""
2035
 
2036
- #: classes/Views/SetupWizard.php:718
2037
  msgid "Knowledge Base & Support Documents"
2038
  msgstr ""
2039
 
2040
- #: classes/Views/SetupWizard.php:723
2041
  msgid "Benefits of keeping a WordPress activity log"
2042
  msgstr ""
2043
 
2044
- #: classes/Views/SetupWizard.php:733
2045
  msgid "We trust this plugin meets all your activity log requirements. Should you encounter any problems, have feature requests or would like to share some feedback"
2046
  msgstr ""
2047
 
2048
- #: classes/Views/SetupWizard.php:733
2049
  msgid "please get in touch!"
2050
  msgstr ""
2051
 
2052
- #: classes/Views/SetupWizard.php:769
2053
  msgid "Third Party Extensions"
2054
  msgstr ""
2055
 
2056
- #: classes/Views/SetupWizard.php:810
2057
  msgid "Monitoring changes done in third party plugins"
2058
  msgstr ""
2059
 
2060
- #: classes/Views/SetupWizard.php:811
2061
  msgid "We noticed that the below plugins are installed on this website. You can install our extensions to also keep a log of changes users do on these plugins."
2062
  msgstr ""
2063
 
@@ -2065,173 +2078,133 @@ msgstr ""
2065
  msgid "Enable/Disable Events"
2066
  msgstr ""
2067
 
2068
- #: classes/Views/ToggleAlerts.php:158
2069
  msgid "Basic"
2070
  msgstr ""
2071
 
2072
- #: classes/Views/ToggleAlerts.php:159
2073
  msgid "Geek"
2074
  msgstr ""
2075
 
2076
- #: classes/Views/ToggleAlerts.php:160
2077
  msgid "Custom"
2078
  msgstr ""
2079
 
2080
- #: classes/Views/ToggleAlerts.php:180
2081
- msgid "Log Level: "
2082
  msgstr ""
2083
 
2084
- #: classes/Views/ToggleAlerts.php:187
2085
- msgid "Use the Log level drop down menu above to use one of our preset log levels. Alternatively you can enable or disable any of the individual events from the below tabs. Refer to <a href=\"https://wpactivitylog.com/support/kb/list-wordpress-activity-log-event-ids/\" target=\"_blank\">the complete list of WordPress activity log event IDs</a> for reference on all the events the plugin can keep a log of."
2086
- msgstr ""
2087
-
2088
- #: classes/Views/ToggleAlerts.php:197
2089
  msgid "Third party plugins"
2090
  msgstr ""
2091
 
2092
- #: classes/Views/ToggleAlerts.php:298
2093
- msgid "Code"
2094
- msgstr ""
2095
-
2096
- #: classes/Views/ToggleAlerts.php:300 classes/WidgetManager.php:79
2097
- msgid "Description"
2098
- msgstr ""
2099
-
2100
- #: classes/Views/ToggleAlerts.php:304 defaults.php:342
2101
- msgid "Content"
2102
- msgstr ""
2103
-
2104
- #: classes/Views/ToggleAlerts.php:307
2105
- msgid "<strong>Note:</strong> Post refers to any type of content, i.e. blog post, page or a post with a custom post type."
2106
- msgstr ""
2107
-
2108
- #: classes/Views/ToggleAlerts.php:310
2109
- msgid "WooCommerce"
2110
- msgstr ""
2111
-
2112
- #: classes/Views/ToggleAlerts.php:310 classes/Views/ToggleAlerts.php:318
2113
- msgid "WooCommerce Products"
2114
- msgstr ""
2115
-
2116
- #: classes/Views/ToggleAlerts.php:314
2117
- msgid "The plugin WooCommerce is not installed on your website so these events have been disabled."
2118
  msgstr ""
2119
 
2120
- #: classes/Views/ToggleAlerts.php:321
2121
- msgid "Products"
2122
  msgstr ""
2123
 
2124
- #: classes/Views/ToggleAlerts.php:327
2125
- msgid "Post Changes"
2126
  msgstr ""
2127
 
2128
- #: classes/Views/ToggleAlerts.php:330 defaults.php:3211
2129
- msgid "MultiSite"
2130
  msgstr ""
2131
 
2132
- #: classes/Views/ToggleAlerts.php:334
2133
- msgid "Your website is a single site so the multisite events have been disabled."
 
2134
  msgstr ""
2135
 
2136
- #: classes/Views/ToggleAlerts.php:338
2137
- msgid "Other User Activity"
2138
  msgstr ""
2139
 
2140
- #: classes/Views/ToggleAlerts.php:341
2141
- msgid "Logins & Logouts"
2142
  msgstr ""
2143
 
2144
- #: classes/Views/ToggleAlerts.php:353 classes/Views/ToggleAlerts.php:512
2145
- #: defaults.php:3362
2146
- msgid "Monitor File Changes"
2147
  msgstr ""
2148
 
2149
- #: classes/Views/ToggleAlerts.php:364
2150
- msgid "Not Implemented"
2151
  msgstr ""
2152
 
2153
- #: classes/Views/ToggleAlerts.php:367
2154
- msgid "Not Available"
2155
  msgstr ""
2156
 
2157
- #: classes/Views/ToggleAlerts.php:382
2158
- msgid "User Logins/Logouts"
2159
  msgstr ""
2160
 
2161
- #: classes/Views/ToggleAlerts.php:385
2162
- msgid "User Sessions"
2163
  msgstr ""
2164
 
2165
  #: classes/Views/ToggleAlerts.php:388
2166
- msgid "Files"
2167
- msgstr ""
2168
-
2169
- #: classes/Views/ToggleAlerts.php:391
2170
- msgid "Post Settings"
2171
- msgstr ""
2172
-
2173
- #: classes/Views/ToggleAlerts.php:422 defaults.php:3361
2174
- msgid "File Changes"
2175
- msgstr ""
2176
-
2177
- #: classes/Views/ToggleAlerts.php:461
2178
  msgid "Keep a log when a visitor registers a user on the website. Only enable this if you allow visitors to register as users on your website. User registration is disabled by default in WordPress."
2179
  msgstr ""
2180
 
2181
- #: classes/Views/ToggleAlerts.php:475 classes/Views/ToggleAlerts.php:488
2182
  msgid "Number of login attempts to log. Enter 0 to log all failed login attempts. (By default the plugin only logs up to 10 failed login because the process can be very resource intensive in case of a brute force attack)"
2183
  msgstr ""
2184
 
2185
- #: classes/Views/ToggleAlerts.php:502
2186
  msgid "Keep a log of user log in activity on custom login forms (such as WooCommerce & membership plugins)"
2187
  msgstr ""
2188
 
2189
- #: classes/Views/ToggleAlerts.php:548
2190
  msgid "Save Changes"
2191
  msgstr ""
2192
 
2193
- #: classes/Views/ToggleAlerts.php:555
2194
  msgid "Log Level Updated"
2195
  msgstr ""
2196
 
2197
- #: classes/Views/ToggleAlerts.php:559
2198
  #, php-format
2199
  msgid "The %s log level has been successfully loaded and applied."
2200
  msgstr ""
2201
 
2202
- #: classes/Views/ToggleAlerts.php:563
2203
  msgid "OK"
2204
  msgstr ""
2205
 
2206
- #: classes/Views/ToggleAlerts.php:578
2207
  msgid "Enable File Integrity Scanner"
2208
  msgstr ""
2209
 
2210
- #: classes/Views/ToggleAlerts.php:580
2211
  msgid "The file integrity scanner is switched off. To enable this event it has to be switched on."
2212
  msgstr ""
2213
 
2214
- #: classes/Views/ToggleAlerts.php:584
2215
  msgid "SWITCH ON"
2216
  msgstr ""
2217
 
2218
- #: classes/Views/ToggleAlerts.php:585
2219
  msgid "DISABLE EVENT"
2220
  msgstr ""
2221
 
2222
- #: classes/Views/addons/html-view.php:95 classes/Views/addons/html-view.php:120
2223
  msgid "Upgrade to Premium"
2224
  msgstr ""
2225
 
2226
- #: classes/Views/addons/html-view.php:96
2227
  msgid "More Information"
2228
  msgstr ""
2229
 
2230
- #: classes/Views/addons/html-view.php:104
2231
  msgid "Screenshots"
2232
  msgstr ""
2233
 
2234
- #: classes/Views/addons/html-view.php:121
2235
  msgid "Start Free 14-Day Premium Trial"
2236
  msgstr ""
2237
 
@@ -2275,8 +2248,8 @@ msgstr ""
2275
  msgid "View menu"
2276
  msgstr ""
2277
 
2278
- #: defaults.php:117 defaults.php:126 defaults.php:3218 defaults.php:3230
2279
- #: defaults.php:3242 defaults.php:3254 defaults.php:3266 defaults.php:3278
2280
  msgid "URL"
2281
  msgstr ""
2282
 
@@ -2392,9 +2365,9 @@ msgstr ""
2392
  #: defaults.php:1746 defaults.php:1760 defaults.php:1774 defaults.php:1789
2393
  #: defaults.php:1804 defaults.php:1818 defaults.php:1832 defaults.php:1848
2394
  #: defaults.php:1863 defaults.php:1877 defaults.php:1891 defaults.php:1906
2395
- #: defaults.php:1922 defaults.php:1936 defaults.php:1951 defaults.php:1965
2396
- #: defaults.php:1979 defaults.php:1996 defaults.php:2010 defaults.php:2024
2397
- #: defaults.php:2397
2398
  msgid "Role"
2399
  msgstr ""
2400
 
@@ -2450,6 +2423,10 @@ msgstr ""
2450
  msgid "Content & Comments"
2451
  msgstr ""
2452
 
 
 
 
 
2453
  #: defaults.php:346
2454
  msgid "User created a new post and saved it as draft"
2455
  msgstr ""
@@ -2468,8 +2445,8 @@ msgstr ""
2468
  #: defaults.php:918 defaults.php:933 defaults.php:950 defaults.php:965
2469
  #: defaults.php:986 defaults.php:1001 defaults.php:1016 defaults.php:1031
2470
  #: defaults.php:1046 defaults.php:1061 defaults.php:1076 defaults.php:1091
2471
- #: defaults.php:1106 defaults.php:1121 defaults.php:1140 defaults.php:2135
2472
- #: defaults.php:2150
2473
  msgid "Post ID"
2474
  msgstr ""
2475
 
@@ -2483,8 +2460,8 @@ msgstr ""
2483
  #: defaults.php:919 defaults.php:934 defaults.php:951 defaults.php:966
2484
  #: defaults.php:987 defaults.php:1002 defaults.php:1017 defaults.php:1032
2485
  #: defaults.php:1047 defaults.php:1062 defaults.php:1077 defaults.php:1092
2486
- #: defaults.php:1107 defaults.php:1122 defaults.php:1141 defaults.php:2136
2487
- #: defaults.php:2151
2488
  msgid "Post type"
2489
  msgstr ""
2490
 
@@ -2498,7 +2475,7 @@ msgstr ""
2498
  #: defaults.php:935 defaults.php:952 defaults.php:967 defaults.php:988
2499
  #: defaults.php:1003 defaults.php:1018 defaults.php:1033 defaults.php:1048
2500
  #: defaults.php:1063 defaults.php:1078 defaults.php:1093 defaults.php:1108
2501
- #: defaults.php:1123 defaults.php:1142 defaults.php:2137 defaults.php:2152
2502
  msgid "Post status"
2503
  msgstr ""
2504
 
@@ -2550,7 +2527,7 @@ msgstr ""
2550
  msgid "Changed the URL of the post %PostTitle%."
2551
  msgstr ""
2552
 
2553
- #: defaults.php:436 defaults.php:2943 defaults.php:2955
2554
  msgid "Previous URL"
2555
  msgstr ""
2556
 
@@ -3704,18 +3681,20 @@ msgstr ""
3704
  #: defaults.php:1719 defaults.php:1733 defaults.php:1747 defaults.php:1761
3705
  #: defaults.php:1775 defaults.php:1790 defaults.php:1805 defaults.php:1819
3706
  #: defaults.php:1833 defaults.php:1849 defaults.php:1878 defaults.php:1892
3707
- #: defaults.php:1907 defaults.php:1923 defaults.php:1937 defaults.php:1952
3708
- #: defaults.php:1966 defaults.php:1980 defaults.php:1997 defaults.php:2011
3709
- #: defaults.php:2025 defaults.php:2039 defaults.php:2052 defaults.php:2398
 
3710
  msgid "First name"
3711
  msgstr ""
3712
 
3713
  #: defaults.php:1720 defaults.php:1734 defaults.php:1748 defaults.php:1762
3714
  #: defaults.php:1776 defaults.php:1791 defaults.php:1806 defaults.php:1820
3715
  #: defaults.php:1834 defaults.php:1850 defaults.php:1865 defaults.php:1893
3716
- #: defaults.php:1908 defaults.php:1924 defaults.php:1938 defaults.php:1953
3717
- #: defaults.php:1967 defaults.php:1981 defaults.php:1998 defaults.php:2012
3718
- #: defaults.php:2026 defaults.php:2040 defaults.php:2053 defaults.php:2399
 
3719
  msgid "Last name"
3720
  msgstr ""
3721
 
@@ -3727,7 +3706,7 @@ msgstr ""
3727
  msgid "Changed the role of user %TargetUsername% to %NewRole%."
3728
  msgstr ""
3729
 
3730
- #: defaults.php:1732 defaults.php:2789
3731
  msgid "Previous role"
3732
  msgstr ""
3733
 
@@ -3791,11 +3770,11 @@ msgstr ""
3791
  msgid "Changed the value of the custom field %custom_field_name% in the user profile %TargetUsername%."
3792
  msgstr ""
3793
 
3794
- #: defaults.php:1835 defaults.php:2879 defaults.php:2911 defaults.php:3322
3795
  msgid "Previous value"
3796
  msgstr ""
3797
 
3798
- #: defaults.php:1836 defaults.php:3323
3799
  msgid "New value"
3800
  msgstr ""
3801
 
@@ -3855,1259 +3834,1316 @@ msgstr ""
3855
  msgid "Previous display name"
3856
  msgstr ""
3857
 
3858
- #: defaults.php:1919 defaults.php:1933
 
 
 
 
 
 
 
 
 
 
 
 
3859
  msgid "User created an application password"
3860
  msgstr ""
3861
 
3862
- #: defaults.php:1920
3863
  msgid "The application password %friendly_name%."
3864
  msgstr ""
3865
 
3866
- #: defaults.php:1934
3867
  msgid "The application password %friendly_name% for the user %login%."
3868
  msgstr ""
3869
 
3870
- #: defaults.php:1948
3871
  msgid "User revoked all application passwords"
3872
  msgstr ""
3873
 
3874
- #: defaults.php:1949
3875
  msgid "All application passwords."
3876
  msgstr ""
3877
 
3878
- #: defaults.php:1962
3879
  msgid "User revoked all application passwords for a user"
3880
  msgstr ""
3881
 
3882
- #: defaults.php:1963
3883
  msgid "All application passwords from the user %login%."
3884
  msgstr ""
3885
 
3886
- #: defaults.php:1976
3887
  msgid "Admin sent a password reset request to a user"
3888
  msgstr ""
3889
 
3890
- #: defaults.php:1977
3891
  msgid "Sent a password reset request to the user %login%."
3892
  msgstr ""
3893
 
3894
- #: defaults.php:1989
3895
  msgid "Multisite User Profiles"
3896
  msgstr ""
3897
 
3898
- #: defaults.php:1993
3899
  msgid "User granted Super Admin privileges"
3900
  msgstr ""
3901
 
3902
- #: defaults.php:1994
3903
  msgid "Granted Super Admin privileges to the user %TargetUsername%."
3904
  msgstr ""
3905
 
3906
- #: defaults.php:2007
3907
  msgid "User revoked from Super Admin privileges"
3908
  msgstr ""
3909
 
3910
- #: defaults.php:2008
3911
  msgid "Revoked Super Admin privileges from %TargetUsername%."
3912
  msgstr ""
3913
 
3914
- #: defaults.php:2021
3915
  msgid "Existing user added to a site"
3916
  msgstr ""
3917
 
3918
- #: defaults.php:2022
3919
  msgid "Added user %TargetUsername% to the site %SiteName%."
3920
  msgstr ""
3921
 
3922
- #: defaults.php:2035
3923
  msgid "User removed from site"
3924
  msgstr ""
3925
 
3926
- #: defaults.php:2036
3927
  msgid "Removed user %TargetUsername% from the site %SiteName%"
3928
  msgstr ""
3929
 
3930
- #: defaults.php:2038
3931
  msgid "Site role"
3932
  msgstr ""
3933
 
3934
- #: defaults.php:2049
3935
  msgid "New network user created"
3936
  msgstr ""
3937
 
3938
- #: defaults.php:2050
3939
  msgid "Created the new network user %NewUserData->Username%."
3940
  msgstr ""
3941
 
3942
- #: defaults.php:2062
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3943
  msgid "Plugins & Themes"
3944
  msgstr ""
3945
 
3946
- #: defaults.php:2067
3947
  msgid "User installed a plugin"
3948
  msgstr ""
3949
 
3950
- #: defaults.php:2068
3951
  msgid "Installed the plugin %Plugin->Name%."
3952
  msgstr ""
3953
 
3954
- #: defaults.php:2070 defaults.php:2083 defaults.php:2096 defaults.php:2109
3955
- #: defaults.php:2205 defaults.php:2218 defaults.php:2231 defaults.php:2270
3956
- #: defaults.php:2283
3957
  msgid "Version"
3958
  msgstr ""
3959
 
3960
- #: defaults.php:2071 defaults.php:2084 defaults.php:2097 defaults.php:2110
3961
- #: defaults.php:2123 defaults.php:2166 defaults.php:2179 defaults.php:2206
3962
- #: defaults.php:2219 defaults.php:2232 defaults.php:2245 defaults.php:2271
3963
- #: defaults.php:2284
3964
  msgid "Install location"
3965
  msgstr ""
3966
 
3967
- #: defaults.php:2080
3968
  msgid "User activated a WordPress plugin"
3969
  msgstr ""
3970
 
3971
- #: defaults.php:2081
3972
  msgid "Activated the plugin %PluginData->Name%."
3973
  msgstr ""
3974
 
3975
- #: defaults.php:2093
3976
  msgid "User deactivated a WordPress plugin"
3977
  msgstr ""
3978
 
3979
- #: defaults.php:2094
3980
  msgid "Deactivated the plugin %PluginData->Name%."
3981
  msgstr ""
3982
 
3983
- #: defaults.php:2106
3984
  msgid "User uninstalled a plugin"
3985
  msgstr ""
3986
 
3987
- #: defaults.php:2107
3988
  msgid "Uninstalled the plugin %PluginData->Name%."
3989
  msgstr ""
3990
 
3991
- #: defaults.php:2119
3992
  msgid "User upgraded a plugin"
3993
  msgstr ""
3994
 
3995
- #: defaults.php:2120
3996
  msgid "Updated the plugin %PluginData->Name%."
3997
  msgstr ""
3998
 
3999
- #: defaults.php:2122
4000
  msgid "Updated version"
4001
  msgstr ""
4002
 
4003
- #: defaults.php:2132
4004
  msgid "A plugin created a post"
4005
  msgstr ""
4006
 
4007
- #: defaults.php:2133
4008
  msgid "The plugin created the post %PostTitle%."
4009
  msgstr ""
4010
 
4011
- #: defaults.php:2147
4012
  msgid "A plugin deleted a post"
4013
  msgstr ""
4014
 
4015
- #: defaults.php:2148
4016
  msgid "A plugin deleted the post %PostTitle%."
4017
  msgstr ""
4018
 
4019
- #: defaults.php:2163
4020
  msgid "Changed the Automatic updates setting for a plugin."
4021
  msgstr ""
4022
 
4023
- #: defaults.php:2164
4024
  msgid "Changed the Automatic updates setting for the plugin %name%."
4025
  msgstr ""
4026
 
4027
- #: defaults.php:2176
4028
  msgid "Changed the Automatic updates setting for a theme."
4029
  msgstr ""
4030
 
4031
- #: defaults.php:2177
4032
  msgid "Changed the Automatic updates setting for the theme %name%."
4033
  msgstr ""
4034
 
4035
- #: defaults.php:2189
4036
  msgid "User changed a file using the plugin editor"
4037
  msgstr ""
4038
 
4039
- #: defaults.php:2190
4040
  msgid "Modified the file %File% with the plugin editor."
4041
  msgstr ""
4042
 
4043
- #: defaults.php:2198
4044
  msgid "Themes"
4045
  msgstr ""
4046
 
4047
- #: defaults.php:2202
4048
  msgid "User installed a theme"
4049
  msgstr ""
4050
 
4051
- #: defaults.php:2203
4052
  msgid "Installed the theme %Theme->Name%."
4053
  msgstr ""
4054
 
4055
- #: defaults.php:2215
4056
  msgid "User activated a theme"
4057
  msgstr ""
4058
 
4059
- #: defaults.php:2216
4060
  msgid "Activated the theme %Theme->Name%."
4061
  msgstr ""
4062
 
4063
- #: defaults.php:2228
4064
  msgid "User uninstalled a theme"
4065
  msgstr ""
4066
 
4067
- #: defaults.php:2229
4068
  msgid "Deleted the theme %Theme->Name%."
4069
  msgstr ""
4070
 
4071
- #: defaults.php:2241
4072
  msgid "User updated a theme"
4073
  msgstr ""
4074
 
4075
- #: defaults.php:2242
4076
  msgid "Updated the theme %Theme->Name%."
4077
  msgstr ""
4078
 
4079
- #: defaults.php:2244 defaults.php:2338
4080
  msgid "New version"
4081
  msgstr ""
4082
 
4083
- #: defaults.php:2254
4084
  msgid "User changed a file using the theme editor"
4085
  msgstr ""
4086
 
4087
- #: defaults.php:2255
4088
  msgid "Modified the file %Theme%/%File% with the theme editor."
4089
  msgstr ""
4090
 
4091
- #: defaults.php:2263
4092
  msgid "Themes on Multisite"
4093
  msgstr ""
4094
 
4095
- #: defaults.php:2267
4096
  msgid "Activated theme on network"
4097
  msgstr ""
4098
 
4099
- #: defaults.php:2268
4100
  msgid "Network activated the theme %Theme->Name%."
4101
  msgstr ""
4102
 
4103
- #: defaults.php:2280
4104
  msgid "Deactivated theme from network"
4105
  msgstr ""
4106
 
4107
- #: defaults.php:2281
4108
  msgid "Network deactivated the theme %Theme->Name%."
4109
  msgstr ""
4110
 
4111
- #: defaults.php:2293
4112
  msgid "WordPress & System"
4113
  msgstr ""
4114
 
4115
- #: defaults.php:2298
4116
  msgid "Unknown Error"
4117
  msgstr ""
4118
 
4119
- #: defaults.php:2299
4120
  msgid "An unexpected error has occurred."
4121
  msgstr ""
4122
 
4123
- #: defaults.php:2304
4124
  msgid "PHP error"
4125
  msgstr ""
4126
 
4127
- #: defaults.php:2305 defaults.php:2311 defaults.php:2317 defaults.php:2323
4128
- #: defaults.php:2329
4129
  msgid "%Message%."
4130
  msgstr ""
4131
 
4132
- #: defaults.php:2310
4133
  msgid "PHP warning"
4134
  msgstr ""
4135
 
4136
- #: defaults.php:2316
4137
  msgid "PHP notice"
4138
  msgstr ""
4139
 
4140
- #: defaults.php:2322
4141
  msgid "PHP exception"
4142
  msgstr ""
4143
 
4144
- #: defaults.php:2328
4145
  msgid "PHP shutdown error"
4146
  msgstr ""
4147
 
4148
- #: defaults.php:2334
4149
  msgid "WordPress was updated"
4150
  msgstr ""
4151
 
4152
- #: defaults.php:2335
4153
  msgid "Updated WordPress."
4154
  msgstr ""
4155
 
4156
- #: defaults.php:2337
4157
  msgid "Previous version"
4158
  msgstr ""
4159
 
4160
- #: defaults.php:2354
4161
  msgid "Advertising Extensions"
4162
  msgstr ""
4163
 
4164
- #: defaults.php:2355
4165
  msgid "%PromoName% %PromoMessage%"
4166
  msgstr ""
4167
 
4168
- #: defaults.php:2359
4169
  msgid "Activity log plugin"
4170
  msgstr ""
4171
 
4172
- #: defaults.php:2363
4173
  msgid "Events automatically pruned by system"
4174
  msgstr ""
4175
 
4176
- #: defaults.php:2364
4177
  msgid "System automatically deleted %EventCount% events from the activity log."
4178
  msgstr ""
4179
 
4180
- #: defaults.php:2373
4181
  msgid "Reset the plugin's settings to default"
4182
  msgstr ""
4183
 
4184
- #: defaults.php:2374
4185
  msgid "Reset the activity log plugin's settings to default."
4186
  msgstr ""
4187
 
4188
- #: defaults.php:2383
4189
  msgid "Purged the activity log"
4190
  msgstr ""
4191
 
4192
- #: defaults.php:2384
4193
  msgid "Purged the activity log."
4194
  msgstr ""
4195
 
4196
- #: defaults.php:2394
4197
  msgid "Deleted all the data about a user from the activity log."
4198
  msgstr ""
4199
 
4200
- #: defaults.php:2395
4201
  msgid "Deleted all the data about the user <strong>%user%</strong> from the activity log."
4202
  msgstr ""
4203
 
4204
- #: defaults.php:2408
4205
  msgid "Deleted all the data of a specific type from the activity log."
4206
  msgstr ""
4207
 
4208
- #: defaults.php:2409
4209
  msgid "Deleted all the data about the %deleted_data_type% %deleted_data% from the activity log."
4210
  msgstr ""
4211
 
4212
- #: defaults.php:2419
4213
  msgid "Some WP Activity Log plugin settings on this site were propagated and overridden from the MainWP dashboard"
4214
  msgstr ""
4215
 
4216
- #: defaults.php:2420
4217
  msgid "Some <strong>WP Activity Log</strong> plugin settings on this site were propagated and overridden from the MainWP dashboard."
4218
  msgstr ""
4219
 
4220
- #: defaults.php:2430
4221
  msgid "Changed the status of the Login Page Notification"
4222
  msgstr ""
4223
 
4224
- #: defaults.php:2431
4225
  msgid "Changed the status of the <strong>Login Page Notification.</strong>"
4226
  msgstr ""
4227
 
4228
- #: defaults.php:2440
4229
  msgid "Changed the text of the Login Page Notification"
4230
  msgstr ""
4231
 
4232
- #: defaults.php:2441
4233
  msgid "Changed the text of the <strong>Login Page Notification.</strong>"
4234
  msgstr ""
4235
 
4236
- #: defaults.php:2450
4237
  msgid "Changed the status of the Reverse proxy / firewall option"
4238
  msgstr ""
4239
 
4240
- #: defaults.php:2451
4241
  msgid "Changed the status of the <strong>Reverse proxy / firewall option.</strong>"
4242
  msgstr ""
4243
 
4244
- #: defaults.php:2460
4245
  msgid "Changed the Restrict plugin access setting"
4246
  msgstr ""
4247
 
4248
- #: defaults.php:2461
4249
  msgid "Changed the <strong>Restrict plugin access</strong> setting to %new_setting%."
4250
  msgstr ""
4251
 
4252
- #: defaults.php:2463 defaults.php:2497 defaults.php:2967 defaults.php:3054
4253
- #: defaults.php:3347
4254
  msgid "Previous setting"
4255
  msgstr ""
4256
 
4257
- #: defaults.php:2472
4258
  msgid "The user %user% to / from the list of users who can view the activity log"
4259
  msgstr ""
4260
 
4261
- #: defaults.php:2473
4262
  msgid "The user %user% to / from the list of users who can view the activity log."
4263
  msgstr ""
4264
 
4265
- #: defaults.php:2475
4266
  msgid "Previous list of users who had access to view the activity log"
4267
  msgstr ""
4268
 
4269
- #: defaults.php:2484
4270
  msgid "Changed the status of the Hide plugin in plugins page setting"
4271
  msgstr ""
4272
 
4273
- #: defaults.php:2485
4274
  msgid "Changed the status of the <strong>Hide plugin in plugins page</strong> setting."
4275
  msgstr ""
4276
 
4277
- #: defaults.php:2494
4278
  msgid "Changed the Activity log retention setting"
4279
  msgstr ""
4280
 
4281
- #: defaults.php:2495
4282
  msgid "Changed the <strong>Activity log retention</strong> to %new_setting%."
4283
  msgstr ""
4284
 
4285
- #: defaults.php:2506
4286
  msgid "A user was added to / from the list of excluded users from the activity log"
4287
  msgstr ""
4288
 
4289
- #: defaults.php:2507
4290
  msgid "The user %user% to / from the list of excluded users from the activity log."
4291
  msgstr ""
4292
 
4293
- #: defaults.php:2509 defaults.php:2521
4294
  msgid "Previous list of users"
4295
  msgstr ""
4296
 
4297
- #: defaults.php:2518
4298
  msgid "A user role was added to / from the list of excluded roles from the activity log"
4299
  msgstr ""
4300
 
4301
- #: defaults.php:2519
4302
  msgid "The user role %role% to / from the list of excluded roles from the activity log."
4303
  msgstr ""
4304
 
4305
- #: defaults.php:2530
4306
  msgid "An IP address was added to / from the list of excluded IP addresses from the activity log"
4307
  msgstr ""
4308
 
4309
- #: defaults.php:2531
4310
  msgid "The IP address %ip% to / from the list of excluded IP addresses from the activity log."
4311
  msgstr ""
4312
 
4313
- #: defaults.php:2533
4314
  msgid "Previous list of IPs"
4315
  msgstr ""
4316
 
4317
- #: defaults.php:2542
4318
  msgid "A post type was added to / from the list of excluded post types from the activity log"
4319
  msgstr ""
4320
 
4321
- #: defaults.php:2543
4322
  msgid "The post type %post_type% to / from the list of excluded post types from the activity log."
4323
  msgstr ""
4324
 
4325
- #: defaults.php:2545
4326
  msgid "Previous list of Post types"
4327
  msgstr ""
4328
 
4329
- #: defaults.php:2554
4330
  msgid "A custom field was added to / from the list of excluded custom fields from the activity log"
4331
  msgstr ""
4332
 
4333
- #: defaults.php:2555
4334
  msgid "The custom field %custom_field% to / from the list of excluded custom fields from the activity log."
4335
  msgstr ""
4336
 
4337
- #: defaults.php:2557
4338
  msgid "Previous list of Custom fields"
4339
  msgstr ""
4340
 
4341
- #: defaults.php:2566
4342
  msgid "A custom field was added to / from the list of excluded user profile custom fields from the activity log"
4343
  msgstr ""
4344
 
4345
- #: defaults.php:2567
4346
  msgid "The custom field %custom_field% to / from the list of excluded user profile custom fields from the activity log."
4347
  msgstr ""
4348
 
4349
- #: defaults.php:2569
4350
  msgid "Previous list of user profile Custom fields"
4351
  msgstr ""
4352
 
4353
- #: defaults.php:2577
4354
  msgid "Notifications & Integrations"
4355
  msgstr ""
4356
 
4357
- #: defaults.php:2581
4358
  msgid "Changed the status of the Daily Summary of Activity Log"
4359
  msgstr ""
4360
 
4361
- #: defaults.php:2582
4362
  msgid "Changed the status of the <strong>Daily Summary of Activity Log.</strong>."
4363
  msgstr ""
4364
 
4365
- #: defaults.php:2591
4366
  msgid "Modified the reciepients of the Daily Summary of Activity Log."
4367
  msgstr ""
4368
 
4369
- #: defaults.php:2592
4370
  msgid "Modified the reciepients of the <strong>Daily Summary of Activity Log</strong>."
4371
  msgstr ""
4372
 
4373
- #: defaults.php:2594
4374
  msgid "New recipient"
4375
  msgstr ""
4376
 
4377
- #: defaults.php:2595
4378
  msgid "Previous recipient"
4379
  msgstr ""
4380
 
4381
- #: defaults.php:2604
4382
  msgid "Changed the status of a built in notification"
4383
  msgstr ""
4384
 
4385
- #: defaults.php:2605
4386
  msgid "Changed the status of the built in notification %notification_name%."
4387
  msgstr ""
4388
 
4389
- #: defaults.php:2614
4390
  msgid "Modified the recipient(s) of the built a notification"
4391
  msgstr ""
4392
 
4393
- #: defaults.php:2615
4394
  msgid "Modified the recipient(s) of the built in notification %notification_name%."
4395
  msgstr ""
4396
 
4397
- #: defaults.php:2617
4398
  msgid "New recipient(s)"
4399
  msgstr ""
4400
 
4401
- #: defaults.php:2618
4402
  msgid "Previous recipient(s)"
4403
  msgstr ""
4404
 
4405
- #: defaults.php:2627
4406
  msgid "Added a new custom notification"
4407
  msgstr ""
4408
 
4409
- #: defaults.php:2628
4410
  msgid "Added a new custom notification %notification_name%."
4411
  msgstr ""
4412
 
4413
- #: defaults.php:2630 defaults.php:2642
4414
  msgid "Recipient(s)"
4415
  msgstr ""
4416
 
4417
- #: defaults.php:2639
4418
  msgid "Modified a custom notification"
4419
  msgstr ""
4420
 
4421
- #: defaults.php:2640
4422
  msgid "Modified the custom notification %notification_name%."
4423
  msgstr ""
4424
 
4425
- #: defaults.php:2651
4426
  msgid "Changed the status of a custom notification"
4427
  msgstr ""
4428
 
4429
- #: defaults.php:2652
4430
  msgid "Changed the status of the custom notification %notification_name%."
4431
  msgstr ""
4432
 
4433
- #: defaults.php:2661
4434
  msgid "Deleted a custom notification"
4435
  msgstr ""
4436
 
4437
- #: defaults.php:2662
4438
  msgid "Deleted the custom notification %notification_name%."
4439
  msgstr ""
4440
 
4441
- #: defaults.php:2671
4442
  msgid "Modified a default notification template"
4443
  msgstr ""
4444
 
4445
- #: defaults.php:2672
4446
  msgid "Modified the default %template_name% notification template."
4447
  msgstr ""
4448
 
4449
- #: defaults.php:2683
4450
  msgid "Added a new integrations connection"
4451
  msgstr ""
4452
 
4453
- #: defaults.php:2684
4454
  msgid "Added / removed the integrations connection %name%"
4455
  msgstr ""
4456
 
4457
- #: defaults.php:2686 defaults.php:2698
4458
  msgid "Connection type"
4459
  msgstr ""
4460
 
4461
- #: defaults.php:2695
4462
  msgid "Modified an integrations connection"
4463
  msgstr ""
4464
 
4465
- #: defaults.php:2696
4466
  msgid "Modified the integrations connection %name%."
4467
  msgstr ""
4468
 
4469
- #: defaults.php:2707
4470
  msgid "Deleted a integrations connection"
4471
  msgstr ""
4472
 
4473
- #: defaults.php:2708
4474
  msgid "Deleted the integrations connection %name%."
4475
  msgstr ""
4476
 
4477
- #: defaults.php:2717
4478
  msgid "Added a new activity log mirror"
4479
  msgstr ""
4480
 
4481
- #: defaults.php:2718
4482
  msgid "Added a new activity log mirror %name%."
4483
  msgstr ""
4484
 
4485
- #: defaults.php:2720 defaults.php:2732 defaults.php:2744
4486
  msgid "Connection used by this mirror"
4487
  msgstr ""
4488
 
4489
- #: defaults.php:2729
4490
  msgid "Modified an activity log mirror"
4491
  msgstr ""
4492
 
4493
- #: defaults.php:2730
4494
  msgid "Modified the activity log mirror %name%."
4495
  msgstr ""
4496
 
4497
- #: defaults.php:2741
4498
  msgid "Changed the status of an activity log mirror"
4499
  msgstr ""
4500
 
4501
- #: defaults.php:2742
4502
  msgid "Changed the status of the activity log mirror %name%."
4503
  msgstr ""
4504
 
4505
- #: defaults.php:2753
4506
  msgid "Deleted an activity log mirror"
4507
  msgstr ""
4508
 
4509
- #: defaults.php:2754
4510
  msgid "Deleted the activity log mirror %name%."
4511
  msgstr ""
4512
 
4513
- #: defaults.php:2763
4514
  msgid "Changed the status of Logging of events to the database"
4515
  msgstr ""
4516
 
4517
- #: defaults.php:2764
4518
  msgid "Changed the status of <strong>Logging of events to the database</strong>."
4519
  msgstr ""
4520
 
4521
- #: defaults.php:2772
4522
  msgid "WordPress Site Settings"
4523
  msgstr ""
4524
 
4525
- #: defaults.php:2776
4526
  msgid "Option Anyone Can Register in WordPress settings changed"
4527
  msgstr ""
4528
 
4529
- #: defaults.php:2777
4530
  msgid "The <strong>Membership</strong> setting <strong>Anyone can register</strong>."
4531
  msgstr ""
4532
 
4533
- #: defaults.php:2786
4534
  msgid "New User Default Role changed"
4535
  msgstr ""
4536
 
4537
- #: defaults.php:2787
4538
  msgid "Changed the <strong>New user default role</strong> WordPress setting."
4539
  msgstr ""
4540
 
4541
- #: defaults.php:2790
4542
  msgid "New role"
4543
  msgstr ""
4544
 
4545
- #: defaults.php:2799
4546
  msgid "WordPress Administrator Notification email changed"
4547
  msgstr ""
4548
 
4549
- #: defaults.php:2800
4550
  msgid "Change the <strong>Administrator email address</strong> in the WordPress settings."
4551
  msgstr ""
4552
 
4553
- #: defaults.php:2802
4554
  msgid "Previous address"
4555
  msgstr ""
4556
 
4557
- #: defaults.php:2803
4558
  msgid "New address"
4559
  msgstr ""
4560
 
4561
- #: defaults.php:2812
4562
  msgid "User changes the WordPress Permalinks"
4563
  msgstr ""
4564
 
4565
- #: defaults.php:2813
4566
  msgid "Changed the <strong>WordPress permalinks</strong>."
4567
  msgstr ""
4568
 
4569
- #: defaults.php:2815
4570
  msgid "Previous permalinks"
4571
  msgstr ""
4572
 
4573
- #: defaults.php:2816
4574
  msgid "New permalinks"
4575
  msgstr ""
4576
 
4577
- #: defaults.php:2825
4578
  msgid "Enabled/Disabled the option Discourage search engines from indexing this site"
4579
  msgstr ""
4580
 
4581
- #: defaults.php:2826
4582
  msgid "Changed the status of the WordPress setting <strong>Search engine visibility</strong> (Discourage search engines from indexing this site)"
4583
  msgstr ""
4584
 
4585
- #: defaults.php:2835
4586
  msgid "Enabled/Disabled comments on all the website"
4587
  msgstr ""
4588
 
4589
- #: defaults.php:2836
4590
  msgid "Changed the status of the WordPress setting <strong>Allow people to submit comments on new posts</strong>."
4591
  msgstr ""
4592
 
4593
- #: defaults.php:2846
4594
  msgid "Enabled/Disabled the option Comment author must fill out name and email"
4595
  msgstr ""
4596
 
4597
- #: defaults.php:2847
4598
  msgid "Changed the status of the WordPress setting <strong>.Comment author must fill out name and email</strong>."
4599
  msgstr ""
4600
 
4601
- #: defaults.php:2856
4602
  msgid "Enabled/Disabled the option Users must be logged in and registered to comment"
4603
  msgstr ""
4604
 
4605
- #: defaults.php:2857
4606
  msgid "Changed the status of the WordPress setting <strong>Users must be registered and logged in to comment</strong>."
4607
  msgstr ""
4608
 
4609
- #: defaults.php:2866
4610
  msgid "Enabled/Disabled the option to automatically close comments"
4611
  msgstr ""
4612
 
4613
- #: defaults.php:2867
4614
  msgid "Changed the status of the WordPress setting <strong>Automatically close comments after %Value% days</strong>."
4615
  msgstr ""
4616
 
4617
- #: defaults.php:2876
4618
  msgid "Changed the value of the option Automatically close comments"
4619
  msgstr ""
4620
 
4621
- #: defaults.php:2877
4622
  msgid "Changed the value of the WordPress setting <strong>Automatically close comments after a number of days</strong> to %NewValue%."
4623
  msgstr ""
4624
 
4625
- #: defaults.php:2888
4626
  msgid "Enabled/Disabled the option for comments to be manually approved"
4627
  msgstr ""
4628
 
4629
- #: defaults.php:2889
4630
  msgid "Changed the value of the WordPress setting <strong>Comments must be manualy approved</strong>."
4631
  msgstr ""
4632
 
4633
- #: defaults.php:2898
4634
  msgid "Enabled/Disabled the option for an author to have previously approved comments for the comments to appear"
4635
  msgstr ""
4636
 
4637
- #: defaults.php:2899
4638
  msgid "Changed the value of the WordPress setting <strong>Comment author must have a previously approved comment</strong>."
4639
  msgstr ""
4640
 
4641
- #: defaults.php:2908
4642
  msgid "Changed the number of links that a comment must have to be held in the queue"
4643
  msgstr ""
4644
 
4645
- #: defaults.php:2909
4646
  msgid "Changed the value of the WordPress setting <strong>Hold a comment in the queue if it contains links</strong> to %NewValue% links."
4647
  msgstr ""
4648
 
4649
- #: defaults.php:2920
4650
  msgid "Modified the list of keywords for comments moderation"
4651
  msgstr ""
4652
 
4653
- #: defaults.php:2921
4654
- msgid "Modified the list of keywords for comments medoration in WordPress."
4655
  msgstr ""
4656
 
4657
- #: defaults.php:2930
4658
  msgid "Modified the list of keywords for comments blacklisting"
4659
  msgstr ""
4660
 
4661
- #: defaults.php:2931
4662
  msgid "Modified the list of <strong>Disallowed comment keys</strong> (keywords) for comments blacklisting in WordPress."
4663
  msgstr ""
4664
 
4665
- #: defaults.php:2940
4666
  msgid "Option WordPress Address (URL) in WordPress settings changed"
4667
  msgstr ""
4668
 
4669
- #: defaults.php:2941
4670
  msgid "Changed the <strong>WordPress address (URL)</strong> tp %new_url%."
4671
  msgstr ""
4672
 
4673
- #: defaults.php:2952
4674
  msgid "Option Site Address (URL) in WordPress settings changed"
4675
  msgstr ""
4676
 
4677
- #: defaults.php:2953
4678
  msgid "Changed the <strong>Site address (URL)</strong> to %new_url%."
4679
  msgstr ""
4680
 
4681
- #: defaults.php:2964
4682
  msgid "Option Your homepage displays in WordPress settings changed"
4683
  msgstr ""
4684
 
4685
- #: defaults.php:2965
4686
  msgid "Changed the <strong>Your homepage displays</strong> WordPress setting to %new_homepage%."
4687
  msgstr ""
4688
 
4689
- #: defaults.php:2976
4690
  msgid "Option homepage in WordPress settings changed"
4691
  msgstr ""
4692
 
4693
- #: defaults.php:2977
4694
  msgid "Changed the <strong>Homepage</strong> in the WordPress settings to %new_page%."
4695
  msgstr ""
4696
 
4697
- #: defaults.php:2979 defaults.php:2991
4698
  msgid "Previous page"
4699
  msgstr ""
4700
 
4701
- #: defaults.php:2988
4702
  msgid "Option posts page in WordPress settings changed"
4703
  msgstr ""
4704
 
4705
- #: defaults.php:2989
4706
  msgid "Changed the <strong> Posts</strong> page in the WordPress settings to %new_page%."
4707
  msgstr ""
4708
 
4709
- #: defaults.php:3001
4710
  msgid "Option Timezone in WordPress settings changed"
4711
  msgstr ""
4712
 
4713
- #: defaults.php:3002
4714
  msgid "Changed the <strong>Timezone</strong> in the WordPress settings to %new_timezone%."
4715
  msgstr ""
4716
 
4717
- #: defaults.php:3004
4718
  msgid "Previous timezone"
4719
  msgstr ""
4720
 
4721
- #: defaults.php:3013
4722
  msgid "Option Date format in WordPress settings changed"
4723
  msgstr ""
4724
 
4725
- #: defaults.php:3014
4726
  msgid "Changed the <strong>Date format</strong> in the WordPress settings to %new_date_format%."
4727
  msgstr ""
4728
 
4729
- #: defaults.php:3016 defaults.php:3028
4730
  msgid "Previous format"
4731
  msgstr ""
4732
 
4733
- #: defaults.php:3025
4734
  msgid "Option Time format in WordPress settings changed"
4735
  msgstr ""
4736
 
4737
- #: defaults.php:3026
4738
  msgid "Changed the <strong>Time format</strong> in the WordPress settings to %new_time_format%."
4739
  msgstr ""
4740
 
4741
- #: defaults.php:3038
4742
  msgid "Option Automatic updates setting changed"
4743
  msgstr ""
4744
 
4745
- #: defaults.php:3039
4746
  msgid "Changed the <strong>Automatic updates</strong> setting."
4747
  msgstr ""
4748
 
4749
- #: defaults.php:3041
4750
  msgid "New setting status"
4751
  msgstr ""
4752
 
4753
- #: defaults.php:3051
4754
  msgid "Option Site Language setting changed"
4755
  msgstr ""
4756
 
4757
- #: defaults.php:3052
4758
  msgid "Changed the <strong>Site Language</strong> to %new_value%."
4759
  msgstr ""
4760
 
4761
- #: defaults.php:3062
 
 
 
 
 
 
 
 
4762
  msgid "Database Events"
4763
  msgstr ""
4764
 
4765
- #: defaults.php:3066
4766
  msgid "Plugin created table"
4767
  msgstr ""
4768
 
4769
- #: defaults.php:3067
4770
  msgid "The plugin %Plugin->Name% created this table in the database."
4771
  msgstr ""
4772
 
4773
- #: defaults.php:3069 defaults.php:3081 defaults.php:3093 defaults.php:3105
4774
- #: defaults.php:3117
4775
  msgid "Table"
4776
  msgstr ""
4777
 
4778
- #: defaults.php:3078
4779
  msgid "Plugin modified table structure"
4780
  msgstr ""
4781
 
4782
- #: defaults.php:3079
4783
  msgid "The plugin %Plugin->Name% modified the structure of a database table."
4784
  msgstr ""
4785
 
4786
- #: defaults.php:3090
4787
  msgid "Plugin deleted table"
4788
  msgstr ""
4789
 
4790
- #: defaults.php:3091
4791
  msgid "The plugin %Plugin->Name% deleted this table from the database."
4792
  msgstr ""
4793
 
4794
- #: defaults.php:3102
4795
  msgid "Theme created tables"
4796
  msgstr ""
4797
 
4798
- #: defaults.php:3103
4799
  msgid "The theme %Theme->Name% created this tables in the database."
4800
  msgstr ""
4801
 
4802
- #: defaults.php:3114
4803
  msgid "Theme modified tables structure"
4804
  msgstr ""
4805
 
4806
- #: defaults.php:3115
4807
  msgid "The theme %Theme->Name% modified the structure of this database table"
4808
  msgstr ""
4809
 
4810
- #: defaults.php:3126
4811
  msgid "Theme deleted tables"
4812
  msgstr ""
4813
 
4814
- #: defaults.php:3127
4815
  msgid "The theme %Theme->Name% deleted this table from the database."
4816
  msgstr ""
4817
 
4818
- #: defaults.php:3129 defaults.php:3141 defaults.php:3153 defaults.php:3165
4819
- #: defaults.php:3177 defaults.php:3189 defaults.php:3201
4820
  msgid "Tables"
4821
  msgstr ""
4822
 
4823
- #: defaults.php:3138
4824
  msgid "Unknown component created tables"
4825
  msgstr ""
4826
 
4827
- #: defaults.php:3139
4828
  msgid "An unknown component created these tables in the database."
4829
  msgstr ""
4830
 
4831
- #: defaults.php:3150
4832
  msgid "Unknown component modified tables structure"
4833
  msgstr ""
4834
 
4835
- #: defaults.php:3151
4836
  msgid "An unknown component modified the structure of these database tables."
4837
  msgstr ""
4838
 
4839
- #: defaults.php:3162
4840
  msgid "Unknown component deleted tables"
4841
  msgstr ""
4842
 
4843
- #: defaults.php:3163
4844
  msgid "An unknown component deleted these tables from the database."
4845
  msgstr ""
4846
 
4847
- #: defaults.php:3174
4848
  msgid "WordPress created tables"
4849
  msgstr ""
4850
 
4851
- #: defaults.php:3175
4852
  msgid "WordPress has created these tables in the database."
4853
  msgstr ""
4854
 
4855
- #: defaults.php:3186
4856
  msgid "WordPress modified tables structure"
4857
  msgstr ""
4858
 
4859
- #: defaults.php:3187
4860
  msgid "WordPress modified the structure of these database tables."
4861
  msgstr ""
4862
 
4863
- #: defaults.php:3198
4864
  msgid "WordPress deleted tables"
4865
  msgstr ""
4866
 
4867
- #: defaults.php:3199
4868
  msgid "WordPress deleted these tables from the database."
4869
  msgstr ""
4870
 
4871
- #: defaults.php:3210
4872
  msgid "Multisite Network Sites"
4873
  msgstr ""
4874
 
4875
- #: defaults.php:3215
 
 
 
 
4876
  msgid "New site added on the network"
4877
  msgstr ""
4878
 
4879
- #: defaults.php:3216
4880
  msgid "Added the new site %SiteName% to the network."
4881
  msgstr ""
4882
 
4883
- #: defaults.php:3227
4884
  msgid "Existing site archived"
4885
  msgstr ""
4886
 
4887
- #: defaults.php:3228
4888
  msgid "Archived the site %SiteName% on the network."
4889
  msgstr ""
4890
 
4891
- #: defaults.php:3239
4892
  msgid "Archived site has been unarchived"
4893
  msgstr ""
4894
 
4895
- #: defaults.php:3240
4896
  msgid "Unarchived the site %SiteName%."
4897
  msgstr ""
4898
 
4899
- #: defaults.php:3251
4900
  msgid "Deactivated site has been activated"
4901
  msgstr ""
4902
 
4903
- #: defaults.php:3252
4904
  msgid "Activated the site %SiteName% on the network."
4905
  msgstr ""
4906
 
4907
- #: defaults.php:3263
4908
  msgid "Site has been deactivated"
4909
  msgstr ""
4910
 
4911
- #: defaults.php:3264
4912
  msgid "Deactiveated the site %SiteName% on the network."
4913
  msgstr ""
4914
 
4915
- #: defaults.php:3275
4916
  msgid "Existing site deleted from network"
4917
  msgstr ""
4918
 
4919
- #: defaults.php:3276
4920
  msgid "The site: %SiteName%."
4921
  msgstr ""
4922
 
4923
- #: defaults.php:3287
4924
  msgid "Allow site administrators to add new users to their sites settings changed"
4925
  msgstr ""
4926
 
4927
- #: defaults.php:3288
4928
  msgid "Changed the status of the network setting <strong>Allow site administrators to add new users to their sites</strong>."
4929
  msgstr ""
4930
 
4931
- #: defaults.php:3297
4932
  msgid "Site upload space settings changed"
4933
  msgstr ""
4934
 
4935
- #: defaults.php:3298
4936
  msgid "Changed the status of the network setting <strong>Site upload space</strong> (to limit space allocated for each site's upload directory)."
4937
  msgstr ""
4938
 
4939
- #: defaults.php:3307
4940
  msgid "Site upload space file size settings changed"
4941
  msgstr ""
4942
 
4943
- #: defaults.php:3308
4944
  msgid "Changed the file size in the <strong>Site upload space</strong> network setting to %new_value%."
4945
  msgstr ""
4946
 
4947
- #: defaults.php:3310
4948
  msgid "Previous size (MB)"
4949
  msgstr ""
4950
 
4951
- #: defaults.php:3319
4952
  msgid "Site Upload file types settings changed"
4953
  msgstr ""
4954
 
4955
- #: defaults.php:3320
4956
  msgid "Changed the network setting <strong>Upload file types (list of allowed file types)</strong>."
4957
  msgstr ""
4958
 
4959
- #: defaults.php:3332
4960
  msgid "Site Max upload file size settings changed"
4961
  msgstr ""
4962
 
4963
- #: defaults.php:3333
4964
  msgid "Changed the <strong>Max upload file size</strong> network setting to %new_value%."
4965
  msgstr ""
4966
 
4967
- #: defaults.php:3335
4968
  msgid "Previous size (KB)"
4969
  msgstr ""
4970
 
4971
- #: defaults.php:3344
4972
  msgid "Allow new registrations settings changed"
4973
  msgstr ""
4974
 
4975
- #: defaults.php:3345
4976
  msgid "Changed the <strong>Allow new registrations</strong> setting to %new_setting%."
4977
  msgstr ""
4978
 
4979
- #: defaults.php:3366
 
 
 
 
 
 
 
 
4980
  msgid "Dummy"
4981
  msgstr ""
4982
 
4983
- #: wp-security-audit-log.php:984 wp-security-audit-log.php:1012
 
 
 
 
 
4984
  #, php-format
4985
  msgid "Hey %s"
4986
  msgstr ""
4987
 
4988
- #: wp-security-audit-log.php:988
4989
  msgid "Never miss an important update! Opt-in to our security and feature updates notifications, and non-sensitive diagnostic tracking with freemius.com."
4990
  msgstr ""
4991
 
4992
- #: wp-security-audit-log.php:989 wp-security-audit-log.php:1024
4993
  msgid "Note: "
4994
  msgstr ""
4995
 
4996
- #: wp-security-audit-log.php:990 wp-security-audit-log.php:1025
4997
  msgid "NO ACTIVITY LOG ACTIVITY & DATA IS SENT BACK TO OUR SERVERS."
4998
  msgstr ""
4999
 
5000
- #: wp-security-audit-log.php:1018
5001
  #, php-format
5002
  msgid "Please help us improve %1$s! If you opt-in, some non-sensitive data about your usage of %2$s will be sent to %3$s, a diagnostic tracking service we use. If you skip this, that's okay! %2$s will still work just fine."
5003
  msgstr ""
5004
 
5005
- #: wp-security-audit-log.php:1070
5006
  #, php-format
5007
  msgid "You need to activate the licence key to use WP Activity Log Premium. %2$s"
5008
  msgstr ""
5009
 
5010
- #: wp-security-audit-log.php:1071
5011
  msgid "Activate the licence key now"
5012
  msgstr ""
5013
 
5014
- #: wp-security-audit-log.php:1089
5015
  #, php-format
5016
  msgid "%s You need to renew your license to continue using premium features."
5017
  msgstr ""
5018
 
5019
- #: wp-security-audit-log.php:1093
5020
  #, php-format
5021
  msgid "The license is limited to %s sub-sites. You need to upgrade your license to cover all the sub-sites on this network."
5022
  msgstr ""
5023
 
5024
- #: wp-security-audit-log.php:1191
5025
  msgid "Error: You do not have sufficient permissions to disable this custom field."
5026
  msgstr ""
5027
 
5028
- #: wp-security-audit-log.php:1239
5029
  #, php-format
5030
  msgid "Custom field %s is no longer being monitored."
5031
  msgstr ""
5032
 
5033
- #: wp-security-audit-log.php:1244
5034
  #, php-format
5035
  msgid "Enable the monitoring of this custom field again from the %s tab in the plugin settings."
5036
  msgstr ""
5037
 
5038
- #: wp-security-audit-log.php:1245
5039
  msgid "Excluded Objects"
5040
  msgstr ""
5041
 
5042
- #: wp-security-audit-log.php:1258
5043
  msgid "Error: You do not have sufficient permissions to disable this alert."
5044
  msgstr ""
5045
 
5046
- #: wp-security-audit-log.php:1282
5047
  #, php-format
5048
  msgid "Alert %1$s is no longer being monitored.<br /> %2$s"
5049
  msgstr ""
5050
 
5051
- #: wp-security-audit-log.php:1282
5052
  msgid "You can enable this alert again from the Enable/Disable Alerts node in the plugin menu."
5053
  msgstr ""
5054
 
5055
- #: wp-security-audit-log.php:1363
5056
  #, php-format
5057
  msgid "You are using a version of PHP that is older than %s, which is no longer supported."
5058
  msgstr ""
5059
 
5060
- #: wp-security-audit-log.php:1365
5061
  msgid "Contact us on <a href=\"mailto:plugins@wpwhitesecurity.com\">plugins@wpwhitesecurity.com</a> to help you switch the version of PHP you are using."
5062
  msgstr ""
5063
 
5064
- #: wp-security-audit-log.php:1370
5065
  #, php-format
5066
  msgid "Please install the %s plugin on the MainWP dashboard."
5067
  msgstr ""
5068
 
5069
- #: wp-security-audit-log.php:1370
5070
  msgid "Activity Log for MainWP"
5071
  msgstr ""
5072
 
5073
- #: wp-security-audit-log.php:1372
5074
  #, php-format
5075
  msgid "The WP Activity Log should be installed on the child sites only. Refer to the %s for more information."
5076
  msgstr ""
5077
 
5078
- #: wp-security-audit-log.php:1372
5079
  msgid "getting started guide"
5080
  msgstr ""
5081
 
5082
- #: wp-security-audit-log.php:1854
5083
  msgid "For security and auditing purposes, a record of all of your logged-in actions and changes within the WordPress dashboard will be recorded in an activity log with the <a href=\"https://wpactivitylog.com/\" target=\"_blank\">WP Activity Log plugin</a>. The audit log also includes the IP address where you accessed this site from."
5084
  msgstr ""
5085
 
5086
- #: wp-security-audit-log.php:1873
5087
  msgid "Every 6 hours"
5088
  msgstr ""
5089
 
5090
- #: wp-security-audit-log.php:1877
5091
  msgid "Every 45 minutes"
5092
  msgstr ""
5093
 
5094
- #: wp-security-audit-log.php:1881
5095
  msgid "Every 30 minutes"
5096
  msgstr ""
5097
 
5098
- #: wp-security-audit-log.php:1885
5099
  msgid "Every 15 minutes"
5100
  msgstr ""
5101
 
5102
- #: wp-security-audit-log.php:1889
5103
  msgid "Every 10 minutes"
5104
  msgstr ""
5105
 
5106
- #: wp-security-audit-log.php:1893
5107
  msgid "Every 1 minute"
5108
  msgstr ""
5109
 
5110
- #: wp-security-audit-log.php:1907
5111
  #, php-format
5112
  msgid "Method %1$s is deprecated since version %2$s!"
5113
  msgstr ""
27
  msgid "Exclude custom field from the monitoring"
28
  msgstr ""
29
 
30
+ #: classes/AlertFormatter.php:104
31
  msgid "unknown"
32
  msgstr ""
33
 
34
+ #: classes/AlertFormatter.php:151
35
  msgid "Download the log file."
36
  msgstr ""
37
 
38
+ #: classes/AlertFormatter.php:159
39
  msgid "published"
40
  msgstr ""
41
 
42
+ #: classes/AlertManager.php:362
43
  #, php-format
44
  msgid "Event with code %d has not be registered."
45
  msgstr ""
46
 
47
+ #: classes/AlertManager.php:445
48
  #, php-format
49
  msgid "Event %s already registered with WP Activity Log."
50
  msgstr ""
51
 
52
+ #: classes/AlertManager.php:490
53
  msgid "You have custom events that are using the same ID or IDs which are already registered in the plugin, so they have been disabled."
54
  msgstr ""
55
 
56
+ #: classes/AlertManager.php:493
57
  #, php-format
58
  msgid "%4$s to help you solve this issue."
59
  msgstr ""
60
 
61
+ #: classes/AlertManager.php:495
62
  msgid "ERROR:"
63
  msgstr ""
64
 
65
+ #: classes/AlertManager.php:497
66
  msgid "Contact us"
67
  msgstr ""
68
 
69
+ #: classes/AlertManager.php:1006 classes/AuditLogListView.php:272
70
  #: classes/AuditLogListView.php:306 classes/Views/Settings.php:1086
71
  #: classes/WidgetManager.php:76 defaults.php:1706
72
  msgid "User"
73
  msgstr ""
74
 
75
+ #: classes/AlertManager.php:1007 classes/AlertManager.php:1373
76
+ #: classes/AuditLogGridView.php:435 classes/AuditLogListView.php:442
77
+ #: defaults.php:2336
78
  msgid "System"
79
  msgstr ""
80
 
81
+ #: classes/AlertManager.php:1008 classes/AuditLogGridView.php:426
82
+ #: classes/AuditLogListView.php:436 defaults.php:2180 defaults.php:2195
83
  msgid "Plugin"
84
  msgstr ""
85
 
86
+ #: classes/AlertManager.php:1009
87
  msgid "Database"
88
  msgstr ""
89
 
90
+ #: classes/AlertManager.php:1010 defaults.php:932
91
  msgid "Post"
92
  msgstr ""
93
 
94
+ #: classes/AlertManager.php:1011 classes/AlertManager.php:1015
95
  msgid "File"
96
  msgstr ""
97
 
98
+ #: classes/AlertManager.php:1012
99
  msgid "Tag"
100
  msgstr ""
101
 
102
+ #: classes/AlertManager.php:1013 defaults.php:79
103
  msgid "Comment"
104
  msgstr ""
105
 
106
+ #: classes/AlertManager.php:1014
107
  msgid "Setting"
108
  msgstr ""
109
 
110
+ #: classes/AlertManager.php:1016
111
  msgid "System Setting"
112
  msgstr ""
113
 
114
+ #: classes/AlertManager.php:1017
115
  msgid "MainWP Network"
116
  msgstr ""
117
 
118
+ #: classes/AlertManager.php:1018
119
  msgid "MainWP"
120
  msgstr ""
121
 
122
+ #: classes/AlertManager.php:1019
123
  msgid "Category"
124
  msgstr ""
125
 
126
+ #: classes/AlertManager.php:1020
127
  msgid "Custom Field"
128
  msgstr ""
129
 
130
+ #: classes/AlertManager.php:1021
131
  msgid "Widget"
132
  msgstr ""
133
 
134
+ #: classes/AlertManager.php:1022
135
  msgid "Menu"
136
  msgstr ""
137
 
138
+ #: classes/AlertManager.php:1023
139
  msgid "Theme"
140
  msgstr ""
141
 
142
+ #: classes/AlertManager.php:1024
143
  msgid "Activity log"
144
  msgstr ""
145
 
146
+ #: classes/AlertManager.php:1025
147
  msgid "WP Activity Log"
148
  msgstr ""
149
 
150
+ #: classes/AlertManager.php:1026
151
  msgid "Multisite Network"
152
  msgstr ""
153
 
154
+ #: classes/AlertManager.php:1027
155
  msgid "IP Address"
156
  msgstr ""
157
 
158
+ #: classes/AlertManager.php:1043
159
  msgid "unknown object"
160
  msgstr ""
161
 
162
+ #: classes/AlertManager.php:1080
163
  msgid "Login"
164
  msgstr ""
165
 
166
+ #: classes/AlertManager.php:1081
167
  msgid "Logout"
168
  msgstr ""
169
 
170
+ #: classes/AlertManager.php:1082
171
  msgid "Installed"
172
  msgstr ""
173
 
174
+ #: classes/AlertManager.php:1083
175
  msgid "Activated"
176
  msgstr ""
177
 
178
+ #: classes/AlertManager.php:1084
179
  msgid "Deactivated"
180
  msgstr ""
181
 
182
+ #: classes/AlertManager.php:1085
183
  msgid "Uninstalled"
184
  msgstr ""
185
 
186
+ #: classes/AlertManager.php:1086
187
  msgid "Updated"
188
  msgstr ""
189
 
190
+ #: classes/AlertManager.php:1087
191
  msgid "Created"
192
  msgstr ""
193
 
194
+ #: classes/AlertManager.php:1088
195
  msgid "Modified"
196
  msgstr ""
197
 
198
+ #: classes/AlertManager.php:1089
199
  msgid "Deleted"
200
  msgstr ""
201
 
202
+ #: classes/AlertManager.php:1090
203
  msgid "Published"
204
  msgstr ""
205
 
206
+ #: classes/AlertManager.php:1091
207
  msgid "Approved"
208
  msgstr ""
209
 
210
+ #: classes/AlertManager.php:1092
211
  msgid "Unapproved"
212
  msgstr ""
213
 
214
+ #: classes/AlertManager.php:1093
215
  msgid "Enabled"
216
  msgstr ""
217
 
218
+ #: classes/AlertManager.php:1094
219
  msgid "Disabled"
220
  msgstr ""
221
 
222
+ #: classes/AlertManager.php:1095
223
  msgid "Added"
224
  msgstr ""
225
 
226
+ #: classes/AlertManager.php:1096
227
  msgid "Failed Login"
228
  msgstr ""
229
 
230
+ #: classes/AlertManager.php:1097
231
  msgid "Blocked"
232
  msgstr ""
233
 
234
+ #: classes/AlertManager.php:1098
235
  msgid "Uploaded"
236
  msgstr ""
237
 
238
+ #: classes/AlertManager.php:1099
239
  msgid "Restored"
240
  msgstr ""
241
 
242
+ #: classes/AlertManager.php:1100
243
  msgid "Opened"
244
  msgstr ""
245
 
246
+ #: classes/AlertManager.php:1101
247
  msgid "Viewed"
248
  msgstr ""
249
 
250
+ #: classes/AlertManager.php:1102
251
  msgid "Started"
252
  msgstr ""
253
 
254
+ #: classes/AlertManager.php:1103
255
  msgid "Stopped"
256
  msgstr ""
257
 
258
+ #: classes/AlertManager.php:1104
259
  msgid "Removed"
260
  msgstr ""
261
 
262
+ #: classes/AlertManager.php:1105
263
  msgid "Unblocked"
264
  msgstr ""
265
 
266
+ #: classes/AlertManager.php:1106
267
  msgid "Renamed"
268
  msgstr ""
269
 
270
+ #: classes/AlertManager.php:1107
271
  msgid "Duplicated"
272
  msgstr ""
273
 
274
+ #: classes/AlertManager.php:1108
275
  msgid "Submitted"
276
  msgstr ""
277
 
278
+ #: classes/AlertManager.php:1109
279
  msgid "Revoked"
280
  msgstr ""
281
 
282
+ #: classes/AlertManager.php:1126
283
  msgid "unknown type"
284
  msgstr ""
285
 
286
+ #: classes/AlertManager.php:1176 classes/Views/ToggleAlerts.php:282
287
+ #: defaults.php:1508
288
  msgid "Pages"
289
  msgstr ""
290
 
291
+ #: classes/AlertManager.php:1176 classes/Views/ToggleAlerts.php:282
292
+ #: defaults.php:1323
293
  msgid "Custom Post Types"
294
  msgstr ""
295
 
296
+ #: classes/AlertManager.php:1273
297
  msgid "System Activity"
298
  msgstr ""
299
 
300
+ #: classes/AlertManager.php:1346 classes/ConstantManager.php:147
301
  msgid "Unknown error code."
302
  msgstr ""
303
 
304
+ #: classes/AlertManager.php:1417 classes/AlertManager.php:1429
305
  msgid "Unknown Site"
306
  msgstr ""
307
 
330
  msgstr ""
331
 
332
  #: classes/AuditLogGridView.php:211 classes/AuditLogListView.php:209
333
+ #: classes/Views/AuditLog.php:593
334
  msgid "All Sites"
335
  msgstr ""
336
 
342
 
343
  #: classes/AuditLogGridView.php:272 classes/AuditLogGridView.php:298
344
  #: classes/AuditLogListView.php:270 classes/AuditLogListView.php:300
345
+ #: classes/Views/Settings.php:1082 classes/Views/ToggleAlerts.php:216
346
+ #: classes/Views/ToggleAlerts.php:256
347
  msgid "Severity"
348
  msgstr ""
349
 
374
  msgstr ""
375
 
376
  #: classes/AuditLogGridView.php:401 classes/AuditLogGridView.php:405
377
+ #: classes/AuditLogListView.php:413 classes/Utilities/UserUtils.php:170
378
  msgid "Unknown"
379
  msgstr ""
380
 
381
+ #: classes/AuditLogGridView.php:429 classes/AuditLogListView.php:438
382
+ #: defaults.php:2105
383
  msgid "Plugins"
384
  msgstr ""
385
 
386
+ #: classes/AuditLogGridView.php:432 classes/AuditLogListView.php:440
387
  msgid "Unregistered user"
388
  msgstr ""
389
 
390
+ #: classes/AuditLogGridView.php:469 classes/AuditLogGridView.php:482
391
  #: classes/AuditLogListView.php:473 classes/AuditLogListView.php:486
392
  msgid "Show me all activity originating from this IP Address"
393
  msgstr ""
394
 
395
+ #: classes/AuditLogGridView.php:511
396
  msgid "Date:"
397
  msgstr ""
398
 
399
+ #: classes/AuditLogGridView.php:515
400
  msgid "Time:"
401
  msgstr ""
402
 
403
+ #: classes/AuditLogGridView.php:519
404
  msgid "User:"
405
  msgstr ""
406
 
407
+ #: classes/AuditLogGridView.php:523
408
  msgid "IP:"
409
  msgstr ""
410
 
411
+ #: classes/AuditLogGridView.php:527
412
  msgid "Object:"
413
  msgstr ""
414
 
415
+ #: classes/AuditLogGridView.php:531
416
  msgid "Event Type:"
417
  msgstr ""
418
 
419
+ #: classes/AuditLogGridView.php:539 classes/AuditLogListView.php:518
420
  msgid "View all details of this change"
421
  msgstr ""
422
 
423
+ #: classes/AuditLogGridView.php:540 classes/AuditLogListView.php:519
424
  msgid "Alert Data Inspector"
425
  msgstr ""
426
 
427
+ #: classes/AuditLogGridView.php:700 classes/AuditLogListView.php:683
428
  msgid "Select All"
429
  msgstr ""
430
 
446
  msgid "Event Type"
447
  msgstr ""
448
 
449
+ #: classes/AuditLogListView.php:382 classes/Models/Occurrence.php:185
450
  msgid "Alert message not found."
451
  msgstr ""
452
 
453
+ #: classes/AuditLogListView.php:383 classes/Models/Occurrence.php:186
454
  msgid "Alert description not found."
455
  msgstr ""
456
 
464
  msgstr ""
465
 
466
  #: classes/ConstantManager.php:159 classes/ConstantManager.php:165
467
+ #: classes/Views/ToggleAlerts.php:352 classes/Views/ToggleAlerts.php:358
468
  msgid "Critical"
469
  msgstr ""
470
 
471
+ #: classes/ConstantManager.php:161 classes/Views/ToggleAlerts.php:354
472
  msgid "Warning"
473
  msgstr ""
474
 
475
+ #: classes/ConstantManager.php:163 classes/Views/ToggleAlerts.php:356
476
+ #: classes/Views/ToggleAlerts.php:368
477
  msgid "Notification"
478
  msgstr ""
479
 
480
+ #: classes/ConstantManager.php:167 classes/Views/ToggleAlerts.php:360
481
  msgid "High"
482
  msgstr ""
483
 
484
+ #: classes/ConstantManager.php:169 classes/Views/ToggleAlerts.php:362
485
  msgid "Medium"
486
  msgstr ""
487
 
488
+ #: classes/ConstantManager.php:171 classes/Views/ToggleAlerts.php:364
489
  msgid "Low"
490
  msgstr ""
491
 
492
+ #: classes/ConstantManager.php:173 classes/Views/ToggleAlerts.php:366
493
  msgid "Informational"
494
  msgstr ""
495
 
496
+ #: classes/Models/Occurrence.php:350
 
 
 
 
497
  #, php-format
498
  msgid "This type of activity / change is no longer monitored. You can create your own custom event IDs to keep a log of such change. Read more about custom events %1$shere%2$s."
499
  msgstr ""
500
 
501
+ #: classes/Sensors/Content.php:755
502
  msgid "Default template"
503
  msgstr ""
504
 
505
+ #: classes/Sensors/Content.php:756
506
  msgid "Default"
507
  msgstr ""
508
 
509
+ #: classes/Sensors/Content.php:797
510
  msgid "No previous image"
511
  msgstr ""
512
 
513
+ #: classes/Sensors/Content.php:798
514
  msgid "No image"
515
  msgstr ""
516
 
517
+ #: classes/Sensors/Content.php:1152 classes/Sensors/Content.php:1160
518
  msgid "Password Protected"
519
  msgstr ""
520
 
521
+ #: classes/Sensors/Content.php:1154 classes/Sensors/Content.php:1162
522
  msgid "Private"
523
  msgstr ""
524
 
525
+ #: classes/Sensors/Content.php:1156 classes/Sensors/Content.php:1164
526
  msgid "Public"
527
  msgstr ""
528
 
529
+ #: classes/Sensors/Content.php:1327
530
  msgid "no tags"
531
  msgstr ""
532
 
533
+ #: classes/Sensors/Multisite.php:81
534
  msgid "disabled"
535
  msgstr ""
536
 
537
+ #: classes/Sensors/Multisite.php:82
538
  msgid "user accounts only"
539
  msgstr ""
540
 
541
+ #: classes/Sensors/Multisite.php:83
542
  msgid "users can register new sites"
543
  msgstr ""
544
 
545
+ #: classes/Sensors/Multisite.php:84
546
  msgid "sites & users can be registered"
547
  msgstr ""
548
 
566
  msgid "This function is deprecated"
567
  msgstr ""
568
 
569
+ #: classes/Settings.php:1919
570
  msgid "Root directory of WordPress (excluding sub directories)"
571
  msgstr ""
572
 
573
+ #: classes/Settings.php:1920
574
  msgid "WP Admin directory (/wp-admin/)"
575
  msgstr ""
576
 
577
+ #: classes/Settings.php:1921
578
  msgid "WP Includes directory (/wp-includes/)"
579
  msgstr ""
580
 
581
+ #: classes/Settings.php:1922
582
  msgid "/wp-content/ directory (excluding plugins, themes & uploads directories)"
583
  msgstr ""
584
 
585
+ #: classes/Settings.php:1923
586
  msgid "Themes directory (/wp-content/themes/)"
587
  msgstr ""
588
 
589
+ #: classes/Settings.php:1924
590
  msgid "Plugins directory (/wp-content/plugins/)"
591
  msgstr ""
592
 
593
+ #: classes/Settings.php:1925
594
  msgid "Uploads directory (/wp-content/uploads/)"
595
  msgstr ""
596
 
597
+ #: classes/Settings.php:1930
598
  msgid "Uploads directory of all sub sites on this network (/wp-content/sites/*)"
599
  msgstr ""
600
 
601
+ #: classes/Settings.php:2082
602
  msgid "None provided"
603
  msgstr ""
604
 
606
  msgid "Keep a record of when someone adds, modifies or deletes forms, entries and more in the Gravity Forms plugin."
607
  msgstr ""
608
 
609
+ #: classes/Upgrade/MetadataMigration.php:65
610
+ msgid "Activity log database update in progress."
611
+ msgstr ""
612
+
613
+ #: classes/Upgrade/MetadataMigration.php:67
614
+ msgid "WP Activity Log is updating the database tables where it stores the activity log. This is needed to upgrade the activity log tables to the new database schema, so the logs can be stored and read more efficiently. The duration of this process varies, depending on the number of events in the activity log. This process runs in the background and won't affect your website. During the upgrade, you might notice some \"null\" values in the activity log. This is temporary until the process is complete."
615
+ msgstr ""
616
+
617
  #: classes/Utilities/Emailer.php:54
618
  #, php-format
619
  msgid "WP Activity Log plugin disabled on %s"
633
  msgstr ""
634
 
635
  #: classes/Utilities/PluginInstallAndActivate.php:101
636
+ #: classes/Views/SetupWizard.php:835
637
  msgid "Extension for "
638
  msgstr ""
639
 
640
  #: classes/Utilities/PluginInstallAndActivate.php:106
641
+ #: classes/Views/SetupWizard.php:840
642
  msgid "Extension installed, activate now?"
643
  msgstr ""
644
 
645
  #: classes/Utilities/PluginInstallAndActivate.php:108
646
+ #: classes/Views/SetupWizard.php:272 classes/Views/SetupWizard.php:842
647
+ #: wp-security-audit-log.php:1195
648
  msgid "Extension installed"
649
  msgstr ""
650
 
651
  #: classes/Utilities/PluginInstallAndActivate.php:110
652
+ #: classes/Views/SetupWizard.php:844
653
  msgid "Install Extension"
654
  msgstr ""
655
 
656
+ #: classes/Utilities/PluginInstallerAction.php:86
657
  msgid "Tried to install a zip or slug that was not in the allowed list"
658
  msgstr ""
659
 
660
+ #: classes/Utilities/UserUtils.php:98
661
  msgid "Username: "
662
  msgstr ""
663
 
664
+ #: classes/Utilities/UserUtils.php:99
665
  msgid "First name: "
666
  msgstr ""
667
 
668
+ #: classes/Utilities/UserUtils.php:100
669
  msgid "Last Name: "
670
  msgstr ""
671
 
672
+ #: classes/Utilities/UserUtils.php:101
673
  msgid "Email: "
674
  msgstr ""
675
 
676
+ #: classes/Utilities/UserUtils.php:102
677
  msgid "Nickname: "
678
  msgstr ""
679
 
680
+ #: classes/ViewManager.php:138
681
  msgid "WP Activity Log requires Website File Changes Monitor 1.6.0. Please upgrade that plugin."
682
  msgstr ""
683
 
684
+ #: classes/ViewManager.php:280
685
  msgid "Free Premium Trial"
686
  msgstr ""
687
 
688
+ #: classes/Views/AuditLog.php:99
689
  msgid "Get email notifications about website changes, view logged-in users, do granular log searches, create detailed reports, and more."
690
  msgstr ""
691
 
692
+ #: classes/Views/AuditLog.php:100
693
  msgid "Upgrade to premium today and get more out of your activity logs!"
694
  msgstr ""
695
 
696
+ #: classes/Views/AuditLog.php:103
697
  msgid "Instant SMS & email alerts, search & filters, reports, users sessions management and much more!"
698
  msgstr ""
699
 
700
+ #: classes/Views/AuditLog.php:104
701
  msgid "Upgrade to premium to get more out of your activity logs!"
702
  msgstr ""
703
 
704
+ #: classes/Views/AuditLog.php:107
705
  msgid "See who logged in on your site in real-time, generate reports, get SMS & email alerts of critical changes and more!"
706
  msgstr ""
707
 
708
+ #: classes/Views/AuditLog.php:108
709
  msgid "Unlock these and other powerful features with WP Activity Log Premium."
710
  msgstr ""
711
 
712
+ #: classes/Views/AuditLog.php:159
713
  msgid "Learn more"
714
  msgstr ""
715
 
749
  msgid "No, thank you"
750
  msgstr ""
751
 
752
+ #: classes/Views/AuditLog.php:276
753
  msgid "We noticed you have"
754
  msgstr ""
755
 
756
+ #: classes/Views/AuditLog.php:278
757
  msgid "installed."
758
  msgstr ""
759
 
760
+ #: classes/Views/AuditLog.php:280
761
  msgid "Install extension"
762
  msgstr ""
763
 
764
+ #: classes/Views/AuditLog.php:325
765
  msgid "Activity Log Viewer"
766
  msgstr ""
767
 
768
+ #: classes/Views/AuditLog.php:352
769
  msgid "Log Viewer"
770
  msgstr ""
771
 
772
+ #: classes/Views/AuditLog.php:509 classes/Views/Settings.php:324
773
+ #: classes/Views/ToggleAlerts.php:112
774
  msgid "You do not have sufficient permissions to access this page."
775
  msgstr ""
776
 
777
+ #: classes/Views/AuditLog.php:558
778
  msgid "Thank you for installing WP Activity Log. Do you want to run the wizard to configure the basic plugin settings?"
779
  msgstr ""
780
 
781
+ #: classes/Views/AuditLog.php:560 classes/Views/Settings.php:532
782
  #: classes/Views/Settings.php:559 classes/Views/Settings.php:625
783
  #: classes/Views/Settings.php:683 classes/Views/Settings.php:1118
784
+ #: classes/Views/Settings.php:1402 classes/Views/Settings.php:1446
785
+ #: classes/Views/Settings.php:1467 classes/Views/Settings.php:1477
786
+ #: classes/Views/SetupWizard.php:563
787
  msgid "Yes"
788
  msgstr ""
789
 
790
+ #: classes/Views/AuditLog.php:561 classes/Views/Settings.php:537
791
  #: classes/Views/Settings.php:564 classes/Views/Settings.php:655
792
  #: classes/Views/Settings.php:693 classes/Views/Settings.php:1123
793
+ #: classes/Views/Settings.php:1409 classes/Views/Settings.php:1453
794
+ #: classes/Views/Settings.php:1468 classes/Views/Settings.php:1478
795
+ #: classes/Views/SetupWizard.php:568
796
  msgid "No"
797
  msgstr ""
798
 
799
+ #: classes/Views/AuditLog.php:592
800
  msgid "Please enter the number of alerts you would like to see on one page:"
801
  msgstr ""
802
 
803
+ #: classes/Views/AuditLog.php:594
804
  msgid "No Results"
805
  msgstr ""
806
 
807
+ #: classes/Views/AuditLog.php:731 classes/Views/AuditLog.php:774
808
+ #: classes/Views/AuditLog.php:1052 classes/Views/AuditLog.php:1114
809
+ #: classes/Views/AuditLog.php:1167 classes/Views/Settings.php:239
810
+ #: classes/Views/Settings.php:1778 classes/Views/SetupWizard.php:96
811
  msgid "Nonce verification failed."
812
  msgstr ""
813
 
814
+ #: classes/Views/AuditLog.php:749
815
  msgid "No users found."
816
  msgstr ""
817
 
818
+ #: classes/Views/AuditLog.php:814
819
  msgid "Freemius opt choice selected."
820
  msgstr ""
821
 
822
+ #: classes/Views/AuditLog.php:821
823
  msgid "Freemius opt choice not found."
824
  msgstr ""
825
 
826
+ #: classes/Views/AuditLog.php:917
827
  #, php-format
828
  msgid "<br>An error occurred when trying to install and activate the plugin. Please try install it again from the %1$sevent settings%2$s page."
829
  msgstr ""
830
 
831
+ #: classes/Views/AuditLog.php:1016
832
  msgid "WordPress Activity Log"
833
  msgstr ""
834
 
835
+ #: classes/Views/AuditLog.php:1017
836
  msgid "When a user makes a change on your website the plugin will keep a record of that event here. Right now there is nothing because this is a new install."
837
  msgstr ""
838
 
839
+ #: classes/Views/AuditLog.php:1018
840
  msgid "Thank you for using WP Activity Log"
841
  msgstr ""
842
 
843
+ #: classes/Views/AuditLog.php:1041 classes/Views/AuditLog.php:1151
844
  msgid "You do not have sufficient permissions to dismiss this notice."
845
  msgstr ""
846
 
847
+ #: classes/Views/AuditLog.php:1109
848
  msgid "Access Denied"
849
  msgstr ""
850
 
851
+ #: classes/Views/AuditLog.php:1215
852
  #, php-format
853
  msgid "Install the activity log extension for %1$s for more detailed logging of changes done in %2$s."
854
  msgstr ""
858
  msgstr ""
859
 
860
  #: classes/Views/EmailNotifications.php:35
861
+ msgid "Email Notifications &#8682;"
862
  msgstr ""
863
 
864
  #: classes/Views/EmailNotifications.php:49
1054
  msgstr ""
1055
 
1056
  #: classes/Views/Help.php:282
1057
+ msgid "Add an extra layer of security to your login pages with 2FA & require your users to use it."
1058
  msgstr ""
1059
 
1060
  #: classes/Views/Help.php:288
1061
+ msgid "Protect website forms & login pages from spambots & automated attacks."
1062
  msgstr ""
1063
 
1064
  #: classes/Views/Help.php:294
1065
+ msgid "Enforce strong password policies on WordPress"
1066
  msgstr ""
1067
 
1068
  #: classes/Views/Help.php:300
1069
+ msgid "Automatically identify unauthorized file changes on WordPress"
1070
  msgstr ""
1071
 
1072
  #: classes/Views/Help.php:306
1073
+ msgid "See the child sites activity logs from the central MainWP dashboard"
1074
+ msgstr ""
1075
+
1076
+ #: classes/Views/Help.php:312
1077
  msgid "Our other WordPress plugins"
1078
  msgstr ""
1079
 
1080
+ #: classes/Views/Help.php:323
1081
  msgid "LEARN MORE"
1082
  msgstr ""
1083
 
1134
  msgstr ""
1135
 
1136
  #: classes/Views/Reports.php:35
1137
+ msgid "Create Reports &#8682;"
1138
  msgstr ""
1139
 
1140
  #: classes/Views/Reports.php:49
1182
  msgstr ""
1183
 
1184
  #: classes/Views/Search.php:35
1185
+ msgid "Log Search &#8682;"
1186
  msgstr ""
1187
 
1188
  #: classes/Views/Search.php:49
1257
  msgid "Unknown settings tab."
1258
  msgstr ""
1259
 
1260
+ #: classes/Views/Settings.php:224 classes/Views/Settings.php:1788
1261
+ #: classes/Views/Settings.php:1811 classes/Views/SetupWizard.php:83
1262
  msgid "Access Denied."
1263
  msgstr ""
1264
 
1270
  msgid "Message sent successfully."
1271
  msgstr ""
1272
 
1273
+ #: classes/Views/Settings.php:340 classes/Views/ToggleAlerts.php:124
1274
  msgid "Settings have been saved."
1275
  msgstr ""
1276
 
1277
+ #: classes/Views/Settings.php:346 classes/Views/ToggleAlerts.php:130
1278
  msgid "Error: "
1279
  msgstr ""
1280
 
1492
  #: classes/Views/Settings.php:834 classes/Views/Settings.php:1217
1493
  #: classes/Views/Settings.php:1238 classes/Views/Settings.php:1259
1494
  #: classes/Views/Settings.php:1281 classes/Views/Settings.php:1329
1495
+ #: classes/Views/Settings.php:1586
1496
  msgid "Remove"
1497
  msgstr ""
1498
 
1648
  msgid "Enable Events for WordPress Background Activity"
1649
  msgstr ""
1650
 
1651
+ #: classes/Views/Settings.php:1172
1652
  msgid "Website File Changes Monitor"
1653
  msgstr ""
1654
 
1655
+ #: classes/Views/Settings.php:1173
1656
  msgid "To keep a log of file changes please install Website File Changes Monitor, a plugin which is also developed by us."
1657
  msgstr ""
1658
 
1659
+ #: classes/Views/Settings.php:1174
1660
  msgid "Install plugin now"
1661
  msgstr ""
1662
 
1663
+ #: classes/Views/Settings.php:1174
1664
  msgid "Learn More"
1665
  msgstr ""
1666
 
1764
  msgid "Enable MainWP Child Site Stealth Mode"
1765
  msgstr ""
1766
 
1767
+ #: classes/Views/Settings.php:1418
1768
  msgid "Admin blocking plugins support"
1769
  msgstr ""
1770
 
1771
+ #: classes/Views/Settings.php:1425
1772
  msgid "Enable early plugin loading on sites that use admin blocking plugins"
1773
  msgstr ""
1774
 
1775
+ #: classes/Views/Settings.php:1434
1776
  msgid "Do you want to delete the plugin data from the database upon uninstall?"
1777
  msgstr ""
1778
 
1779
+ #: classes/Views/Settings.php:1435
1780
  msgid "The plugin saves the activity log data and settings in the WordPress database. By default upon uninstalling the plugin the data is kept in the database so if it is installed again, you can still access the data. If the data is deleted it is not possible to recover it so you won't be able to access it again even when you reinstall the plugin."
1781
  msgstr ""
1782
 
1783
+ #: classes/Views/Settings.php:1439
1784
  msgid "Remove Data on Uninstall"
1785
  msgstr ""
1786
 
1787
+ #: classes/Views/Settings.php:1464
1788
  msgid "Are you sure you want to reset all the plugin settings to default? This action cannot be undone."
1789
  msgstr ""
1790
 
1791
+ #: classes/Views/Settings.php:1474
1792
  msgid "Are you sure you want to purge all the activity log data?"
1793
  msgstr ""
1794
 
1795
+ #: classes/Views/Settings.php:1498
1796
  msgid "MainWP Child plugin is not active on this website."
1797
  msgstr ""
1798
 
1799
+ #: classes/Views/Settings.php:1579
1800
  msgid "The specified value is not a valid URL!"
1801
  msgstr ""
1802
 
1803
+ #: classes/Views/Settings.php:1580
1804
  msgid "The specified value is not a valid post type!"
1805
  msgstr ""
1806
 
1807
+ #: classes/Views/Settings.php:1581
1808
  msgid "The specified value is not a valid IP address!"
1809
  msgstr ""
1810
 
1811
+ #: classes/Views/Settings.php:1582
1812
  msgid "The specified value is not a user nor a role!"
1813
  msgstr ""
1814
 
1815
+ #: classes/Views/Settings.php:1583
1816
  msgid "Filename cannot be added because it contains invalid characters."
1817
  msgstr ""
1818
 
1819
+ #: classes/Views/Settings.php:1584
1820
  msgid "File extension cannot be added because it contains invalid characters."
1821
  msgstr ""
1822
 
1823
+ #: classes/Views/Settings.php:1585
1824
  msgid "Directory cannot be added because it contains invalid characters."
1825
  msgstr ""
1826
 
1827
+ #: classes/Views/Settings.php:1587
1828
  msgid "Please save any changes before switching tabs."
1829
  msgstr ""
1830
 
1831
+ #: classes/Views/Settings.php:1794 classes/Views/Settings.php:1817
1832
  msgid "Nonce Verification Failed."
1833
  msgstr ""
1834
 
1835
+ #: classes/Views/Settings.php:1802
1836
  msgid "Plugin settings have been reset."
1837
  msgstr ""
1838
 
1839
+ #: classes/Views/Settings.php:1826
1840
  msgid "Tables has been reset."
1841
  msgstr ""
1842
 
1843
+ #: classes/Views/Settings.php:1828
1844
  msgid "Reset query failed."
1845
  msgstr ""
1846
 
1847
+ #: classes/Views/Settings.php:1841
1848
  msgid "Activity log retention"
1849
  msgstr ""
1850
 
1851
+ #: classes/Views/Settings.php:1847
1852
  msgid "Keep all data"
1853
  msgstr ""
1854
 
1855
+ #: classes/Views/Settings.php:1863
1856
  msgid "Days"
1857
  msgstr ""
1858
 
1859
+ #: classes/Views/Settings.php:1864
1860
  msgid "Months"
1861
  msgstr ""
1862
 
1863
+ #: classes/Views/Settings.php:1865
1864
  msgid "Years"
1865
  msgstr ""
1866
 
1867
+ #: classes/Views/Settings.php:1880
1868
  msgid "Delete events older than"
1869
  msgstr ""
1870
 
1871
+ #: classes/Views/Settings.php:1898
1872
  msgid "The next scheduled purging of activity log data that is older than "
1873
  msgstr ""
1874
 
1875
+ #: classes/Views/Settings.php:1905
1876
  msgid "You can run the purging process now by clicking the button below."
1877
  msgstr ""
1878
 
1879
+ #: classes/Views/Settings.php:1909
1880
  msgid "Purge Old Data"
1881
  msgstr ""
1882
 
1900
  msgid "Log Retention"
1901
  msgstr ""
1902
 
1903
+ #: classes/Views/SetupWizard.php:202 classes/Views/SetupWizard.php:742
1904
+ #: classes/Views/SetupWizard.php:743
1905
  msgid "Finish"
1906
  msgstr ""
1907
 
1917
  msgid "Specified value in not an IP address."
1918
  msgstr ""
1919
 
1920
+ #: classes/Views/SetupWizard.php:270 wp-security-audit-log.php:1193
1921
  msgid "Installing, please wait"
1922
  msgstr ""
1923
 
1924
+ #: classes/Views/SetupWizard.php:271 wp-security-audit-log.php:1194
1925
  msgid "Already installed"
1926
  msgstr ""
1927
 
1928
+ #: classes/Views/SetupWizard.php:273 wp-security-audit-log.php:1196
1929
  msgid "Extension activated"
1930
  msgstr ""
1931
 
1932
+ #: classes/Views/SetupWizard.php:274 wp-security-audit-log.php:1197
1933
  msgid "Install failed"
1934
  msgstr ""
1935
 
1936
+ #: classes/Views/SetupWizard.php:275 wp-security-audit-log.php:1198
1937
+ msgid "Reloading page"
1938
+ msgstr ""
1939
+
1940
+ #: classes/Views/SetupWizard.php:305
1941
  msgid "WP Activity Log &rsaquo; Setup Wizard"
1942
  msgstr ""
1943
 
1944
+ #: classes/Views/SetupWizard.php:323
1945
  msgid "Close Wizard"
1946
  msgstr ""
1947
 
1948
+ #: classes/Views/SetupWizard.php:416
1949
  #, php-format
1950
  msgid "You have reached an invaild step - %1$sreturn to the start of the wizard%2$s."
1951
  msgstr ""
1952
 
1953
+ #: classes/Views/SetupWizard.php:433
1954
  msgid "This wizard helps you configure the basic plugin settings. All these settings can be changed at a later stage from the plugin settings."
1955
  msgstr ""
1956
 
1957
+ #: classes/Views/SetupWizard.php:438
1958
  msgid "Start Configuring the Plugin"
1959
  msgstr ""
1960
 
1961
+ #: classes/Views/SetupWizard.php:442
1962
  msgid "Exit Wizard"
1963
  msgstr ""
1964
 
1965
+ #: classes/Views/SetupWizard.php:455
1966
  msgid "Please select the level of detail for your WordPress activity logs:"
1967
  msgstr ""
1968
 
1969
+ #: classes/Views/SetupWizard.php:459
1970
  msgid "Basic (I want a high level overview and I am not interested in the detail)"
1971
  msgstr ""
1972
 
1973
+ #: classes/Views/SetupWizard.php:464
1974
  msgid "Geek (I want to know everything that is happening on my WordPress)"
1975
  msgstr ""
1976
 
1977
+ #: classes/Views/SetupWizard.php:466
1978
  msgid "Note: You can change the WordPress logging level from the plugin’s settings anytime."
1979
  msgstr ""
1980
 
1981
+ #: classes/Views/SetupWizard.php:469 classes/Views/SetupWizard.php:526
1982
+ #: classes/Views/SetupWizard.php:575 classes/Views/SetupWizard.php:635
1983
+ #: classes/Views/SetupWizard.php:636 classes/Views/SetupWizard.php:857
1984
+ #: classes/Views/SetupWizard.php:858
1985
  msgid "Next"
1986
  msgstr ""
1987
 
1988
+ #: classes/Views/SetupWizard.php:510
1989
  msgid "Do you or your users use other pages to log in to WordPress other than the default login page ( /wp-admin/ )?"
1990
  msgstr ""
1991
 
1992
+ #: classes/Views/SetupWizard.php:514
1993
  msgid "Yes, we use other pages to login to WordPress."
1994
  msgstr ""
1995
 
1996
+ #: classes/Views/SetupWizard.php:519
1997
  msgid "No, we only use the default WordPress login page."
1998
  msgstr ""
1999
 
2000
+ #: classes/Views/SetupWizard.php:521
2001
  msgid "If your website is a membership or ecommerce website most probably you have more than one area from where the users can login. If you are not sure, select Yes."
2002
  msgstr ""
2003
 
2004
+ #: classes/Views/SetupWizard.php:524 classes/Views/SetupWizard.php:573
2005
+ #: classes/Views/SetupWizard.php:627
2006
  msgid "Note: You can change the WordPress activity log retention settings at any time from the plugin settings later on."
2007
  msgstr ""
2008
 
2009
+ #: classes/Views/SetupWizard.php:559
2010
  msgid "Can visitors register as a user on your website?"
2011
  msgstr ""
2012
 
2013
+ #: classes/Views/SetupWizard.php:570
2014
  msgid "If you are not sure about this setting, check if the Membership setting in the WordPress General settings is checked or not. If it is not checked (default) select No."
2015
  msgstr ""
2016
 
2017
+ #: classes/Views/SetupWizard.php:609
2018
  msgid "How long do you want to keep the data in the WordPress activity Log?"
2019
  msgstr ""
2020
 
2021
+ #: classes/Views/SetupWizard.php:614
2022
  msgid "6 months (data older than 6 months will be deleted)"
2023
  msgstr ""
2024
 
2025
+ #: classes/Views/SetupWizard.php:619
2026
  msgid "12 months (data older than 12 months will be deleted)"
2027
  msgstr ""
2028
 
2029
+ #: classes/Views/SetupWizard.php:624
2030
  msgid "Keep all data."
2031
  msgstr ""
2032
 
2033
+ #: classes/Views/SetupWizard.php:645
2034
  msgid "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://wpactivitylog.com/features/?utm_source=plugin&utm_medium=referral&utm_campaign=WSAL&utm_content=wizard+configuration\" target=\"_blank\">upgrade to Premium</a> and use the Database tools to store the WordPress activity log in an external database."
2035
  msgstr ""
2036
 
2037
+ #: classes/Views/SetupWizard.php:708
2038
  msgid "Your plugin is all set and it is ready to start keeping a record of everything that is happening on your WordPress in a WordPress activity log."
2039
  msgstr ""
2040
 
2041
+ #: classes/Views/SetupWizard.php:709
2042
  msgid "Below are a few useful links you might need to refer to:"
2043
  msgstr ""
2044
 
2045
+ #: classes/Views/SetupWizard.php:714
2046
  msgid "Getting started with the WP Activity Log plugin"
2047
  msgstr ""
2048
 
2049
+ #: classes/Views/SetupWizard.php:719
2050
  msgid "Knowledge Base & Support Documents"
2051
  msgstr ""
2052
 
2053
+ #: classes/Views/SetupWizard.php:724
2054
  msgid "Benefits of keeping a WordPress activity log"
2055
  msgstr ""
2056
 
2057
+ #: classes/Views/SetupWizard.php:734
2058
  msgid "We trust this plugin meets all your activity log requirements. Should you encounter any problems, have feature requests or would like to share some feedback"
2059
  msgstr ""
2060
 
2061
+ #: classes/Views/SetupWizard.php:734
2062
  msgid "please get in touch!"
2063
  msgstr ""
2064
 
2065
+ #: classes/Views/SetupWizard.php:770
2066
  msgid "Third Party Extensions"
2067
  msgstr ""
2068
 
2069
+ #: classes/Views/SetupWizard.php:811
2070
  msgid "Monitoring changes done in third party plugins"
2071
  msgstr ""
2072
 
2073
+ #: classes/Views/SetupWizard.php:812
2074
  msgid "We noticed that the below plugins are installed on this website. You can install our extensions to also keep a log of changes users do on these plugins."
2075
  msgstr ""
2076
 
2078
  msgid "Enable/Disable Events"
2079
  msgstr ""
2080
 
2081
+ #: classes/Views/ToggleAlerts.php:161
2082
  msgid "Basic"
2083
  msgstr ""
2084
 
2085
+ #: classes/Views/ToggleAlerts.php:162
2086
  msgid "Geek"
2087
  msgstr ""
2088
 
2089
+ #: classes/Views/ToggleAlerts.php:163
2090
  msgid "Custom"
2091
  msgstr ""
2092
 
2093
+ #: classes/Views/ToggleAlerts.php:181
2094
+ msgid "Enable/Disable alerts"
2095
  msgstr ""
2096
 
2097
+ #: classes/Views/ToggleAlerts.php:183
 
 
 
 
2098
  msgid "Third party plugins"
2099
  msgstr ""
2100
 
2101
+ #: classes/Views/ToggleAlerts.php:194
2102
+ msgid "Log Level: "
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2103
  msgstr ""
2104
 
2105
+ #: classes/Views/ToggleAlerts.php:201
2106
+ msgid "Use the Log level drop down menu above to use one of our preset log levels. Alternatively you can enable or disable any of the individual events from the below tabs. Refer to <a href=\"https://wpactivitylog.com/support/kb/list-wordpress-activity-log-event-ids/\" target=\"_blank\">the complete list of WordPress activity log event IDs</a> for reference on all the events the plugin can keep a log of."
2107
  msgstr ""
2108
 
2109
+ #: classes/Views/ToggleAlerts.php:213
2110
+ msgid "Choose your query and enter your search term"
2111
  msgstr ""
2112
 
2113
+ #: classes/Views/ToggleAlerts.php:215
2114
+ msgid "Event code"
2115
  msgstr ""
2116
 
2117
+ #: classes/Views/ToggleAlerts.php:217 classes/Views/ToggleAlerts.php:257
2118
+ #: classes/WidgetManager.php:79
2119
+ msgid "Description"
2120
  msgstr ""
2121
 
2122
+ #: classes/Views/ToggleAlerts.php:221
2123
+ msgid "OR"
2124
  msgstr ""
2125
 
2126
+ #: classes/Views/ToggleAlerts.php:223
2127
+ msgid "Choose a category"
2128
  msgstr ""
2129
 
2130
+ #: classes/Views/ToggleAlerts.php:225
2131
+ msgid "All categories"
 
2132
  msgstr ""
2133
 
2134
+ #: classes/Views/ToggleAlerts.php:255
2135
+ msgid "Code"
2136
  msgstr ""
2137
 
2138
+ #: classes/Views/ToggleAlerts.php:306
2139
+ msgid "Please activate WooCommerce to enable this alert"
2140
  msgstr ""
2141
 
2142
+ #: classes/Views/ToggleAlerts.php:314
2143
+ msgid "Please activate the Yoast SEO Plugin"
2144
  msgstr ""
2145
 
2146
+ #: classes/Views/ToggleAlerts.php:322
2147
+ msgid "Your website is a single site so the multisite events have been disabled."
2148
  msgstr ""
2149
 
2150
  #: classes/Views/ToggleAlerts.php:388
 
 
 
 
 
 
 
 
 
 
 
 
2151
  msgid "Keep a log when a visitor registers a user on the website. Only enable this if you allow visitors to register as users on your website. User registration is disabled by default in WordPress."
2152
  msgstr ""
2153
 
2154
+ #: classes/Views/ToggleAlerts.php:402 classes/Views/ToggleAlerts.php:415
2155
  msgid "Number of login attempts to log. Enter 0 to log all failed login attempts. (By default the plugin only logs up to 10 failed login because the process can be very resource intensive in case of a brute force attack)"
2156
  msgstr ""
2157
 
2158
+ #: classes/Views/ToggleAlerts.php:428
2159
  msgid "Keep a log of user log in activity on custom login forms (such as WooCommerce & membership plugins)"
2160
  msgstr ""
2161
 
2162
+ #: classes/Views/ToggleAlerts.php:443
2163
  msgid "Save Changes"
2164
  msgstr ""
2165
 
2166
+ #: classes/Views/ToggleAlerts.php:458
2167
  msgid "Log Level Updated"
2168
  msgstr ""
2169
 
2170
+ #: classes/Views/ToggleAlerts.php:462
2171
  #, php-format
2172
  msgid "The %s log level has been successfully loaded and applied."
2173
  msgstr ""
2174
 
2175
+ #: classes/Views/ToggleAlerts.php:466
2176
  msgid "OK"
2177
  msgstr ""
2178
 
2179
+ #: classes/Views/ToggleAlerts.php:481
2180
  msgid "Enable File Integrity Scanner"
2181
  msgstr ""
2182
 
2183
+ #: classes/Views/ToggleAlerts.php:483
2184
  msgid "The file integrity scanner is switched off. To enable this event it has to be switched on."
2185
  msgstr ""
2186
 
2187
+ #: classes/Views/ToggleAlerts.php:487
2188
  msgid "SWITCH ON"
2189
  msgstr ""
2190
 
2191
+ #: classes/Views/ToggleAlerts.php:488
2192
  msgid "DISABLE EVENT"
2193
  msgstr ""
2194
 
2195
+ #: classes/Views/addons/html-view.php:97 classes/Views/addons/html-view.php:122
2196
  msgid "Upgrade to Premium"
2197
  msgstr ""
2198
 
2199
+ #: classes/Views/addons/html-view.php:98
2200
  msgid "More Information"
2201
  msgstr ""
2202
 
2203
+ #: classes/Views/addons/html-view.php:106
2204
  msgid "Screenshots"
2205
  msgstr ""
2206
 
2207
+ #: classes/Views/addons/html-view.php:123
2208
  msgid "Start Free 14-Day Premium Trial"
2209
  msgstr ""
2210
 
2248
  msgid "View menu"
2249
  msgstr ""
2250
 
2251
+ #: defaults.php:117 defaults.php:126 defaults.php:3273 defaults.php:3285
2252
+ #: defaults.php:3297 defaults.php:3309 defaults.php:3321 defaults.php:3333
2253
  msgid "URL"
2254
  msgstr ""
2255
 
2365
  #: defaults.php:1746 defaults.php:1760 defaults.php:1774 defaults.php:1789
2366
  #: defaults.php:1804 defaults.php:1818 defaults.php:1832 defaults.php:1848
2367
  #: defaults.php:1863 defaults.php:1877 defaults.php:1891 defaults.php:1906
2368
+ #: defaults.php:1921 defaults.php:1938 defaults.php:1952 defaults.php:1967
2369
+ #: defaults.php:1981 defaults.php:1995 defaults.php:2012 defaults.php:2026
2370
+ #: defaults.php:2040 defaults.php:2081 defaults.php:2439
2371
  msgid "Role"
2372
  msgstr ""
2373
 
2423
  msgid "Content & Comments"
2424
  msgstr ""
2425
 
2426
+ #: defaults.php:342
2427
+ msgid "Content"
2428
+ msgstr ""
2429
+
2430
  #: defaults.php:346
2431
  msgid "User created a new post and saved it as draft"
2432
  msgstr ""
2445
  #: defaults.php:918 defaults.php:933 defaults.php:950 defaults.php:965
2446
  #: defaults.php:986 defaults.php:1001 defaults.php:1016 defaults.php:1031
2447
  #: defaults.php:1046 defaults.php:1061 defaults.php:1076 defaults.php:1091
2448
+ #: defaults.php:1106 defaults.php:1121 defaults.php:1140 defaults.php:2177
2449
+ #: defaults.php:2192
2450
  msgid "Post ID"
2451
  msgstr ""
2452
 
2460
  #: defaults.php:919 defaults.php:934 defaults.php:951 defaults.php:966
2461
  #: defaults.php:987 defaults.php:1002 defaults.php:1017 defaults.php:1032
2462
  #: defaults.php:1047 defaults.php:1062 defaults.php:1077 defaults.php:1092
2463
+ #: defaults.php:1107 defaults.php:1122 defaults.php:1141 defaults.php:2178
2464
+ #: defaults.php:2193
2465
  msgid "Post type"
2466
  msgstr ""
2467
 
2475
  #: defaults.php:935 defaults.php:952 defaults.php:967 defaults.php:988
2476
  #: defaults.php:1003 defaults.php:1018 defaults.php:1033 defaults.php:1048
2477
  #: defaults.php:1063 defaults.php:1078 defaults.php:1093 defaults.php:1108
2478
+ #: defaults.php:1123 defaults.php:1142 defaults.php:2179 defaults.php:2194
2479
  msgid "Post status"
2480
  msgstr ""
2481
 
2527
  msgid "Changed the URL of the post %PostTitle%."
2528
  msgstr ""
2529
 
2530
+ #: defaults.php:436 defaults.php:2985 defaults.php:2997
2531
  msgid "Previous URL"
2532
  msgstr ""
2533
 
3681
  #: defaults.php:1719 defaults.php:1733 defaults.php:1747 defaults.php:1761
3682
  #: defaults.php:1775 defaults.php:1790 defaults.php:1805 defaults.php:1819
3683
  #: defaults.php:1833 defaults.php:1849 defaults.php:1878 defaults.php:1892
3684
+ #: defaults.php:1907 defaults.php:1922 defaults.php:1939 defaults.php:1953
3685
+ #: defaults.php:1968 defaults.php:1982 defaults.php:1996 defaults.php:2013
3686
+ #: defaults.php:2027 defaults.php:2041 defaults.php:2055 defaults.php:2068
3687
+ #: defaults.php:2082 defaults.php:2440
3688
  msgid "First name"
3689
  msgstr ""
3690
 
3691
  #: defaults.php:1720 defaults.php:1734 defaults.php:1748 defaults.php:1762
3692
  #: defaults.php:1776 defaults.php:1791 defaults.php:1806 defaults.php:1820
3693
  #: defaults.php:1834 defaults.php:1850 defaults.php:1865 defaults.php:1893
3694
+ #: defaults.php:1908 defaults.php:1923 defaults.php:1940 defaults.php:1954
3695
+ #: defaults.php:1969 defaults.php:1983 defaults.php:1997 defaults.php:2014
3696
+ #: defaults.php:2028 defaults.php:2042 defaults.php:2056 defaults.php:2069
3697
+ #: defaults.php:2083 defaults.php:2441
3698
  msgid "Last name"
3699
  msgstr ""
3700
 
3706
  msgid "Changed the role of user %TargetUsername% to %NewRole%."
3707
  msgstr ""
3708
 
3709
+ #: defaults.php:1732 defaults.php:2831
3710
  msgid "Previous role"
3711
  msgstr ""
3712
 
3770
  msgid "Changed the value of the custom field %custom_field_name% in the user profile %TargetUsername%."
3771
  msgstr ""
3772
 
3773
+ #: defaults.php:1835 defaults.php:2921 defaults.php:2953 defaults.php:3377
3774
  msgid "Previous value"
3775
  msgstr ""
3776
 
3777
+ #: defaults.php:1836 defaults.php:3378
3778
  msgid "New value"
3779
  msgstr ""
3780
 
3834
  msgid "Previous display name"
3835
  msgstr ""
3836
 
3837
+ #: defaults.php:1918
3838
+ msgid "User's website URL was modified"
3839
+ msgstr ""
3840
+
3841
+ #: defaults.php:1919
3842
+ msgid "Changed the website URL of the user %TargetUsername% to %new_url%."
3843
+ msgstr ""
3844
+
3845
+ #: defaults.php:1924
3846
+ msgid "Previous website URL"
3847
+ msgstr ""
3848
+
3849
+ #: defaults.php:1935 defaults.php:1949
3850
  msgid "User created an application password"
3851
  msgstr ""
3852
 
3853
+ #: defaults.php:1936
3854
  msgid "The application password %friendly_name%."
3855
  msgstr ""
3856
 
3857
+ #: defaults.php:1950
3858
  msgid "The application password %friendly_name% for the user %login%."
3859
  msgstr ""
3860
 
3861
+ #: defaults.php:1964
3862
  msgid "User revoked all application passwords"
3863
  msgstr ""
3864
 
3865
+ #: defaults.php:1965
3866
  msgid "All application passwords."
3867
  msgstr ""
3868
 
3869
+ #: defaults.php:1978
3870
  msgid "User revoked all application passwords for a user"
3871
  msgstr ""
3872
 
3873
+ #: defaults.php:1979
3874
  msgid "All application passwords from the user %login%."
3875
  msgstr ""
3876
 
3877
+ #: defaults.php:1992
3878
  msgid "Admin sent a password reset request to a user"
3879
  msgstr ""
3880
 
3881
+ #: defaults.php:1993
3882
  msgid "Sent a password reset request to the user %login%."
3883
  msgstr ""
3884
 
3885
+ #: defaults.php:2005
3886
  msgid "Multisite User Profiles"
3887
  msgstr ""
3888
 
3889
+ #: defaults.php:2009
3890
  msgid "User granted Super Admin privileges"
3891
  msgstr ""
3892
 
3893
+ #: defaults.php:2010
3894
  msgid "Granted Super Admin privileges to the user %TargetUsername%."
3895
  msgstr ""
3896
 
3897
+ #: defaults.php:2023
3898
  msgid "User revoked from Super Admin privileges"
3899
  msgstr ""
3900
 
3901
+ #: defaults.php:2024
3902
  msgid "Revoked Super Admin privileges from %TargetUsername%."
3903
  msgstr ""
3904
 
3905
+ #: defaults.php:2037
3906
  msgid "Existing user added to a site"
3907
  msgstr ""
3908
 
3909
+ #: defaults.php:2038
3910
  msgid "Added user %TargetUsername% to the site %SiteName%."
3911
  msgstr ""
3912
 
3913
+ #: defaults.php:2051
3914
  msgid "User removed from site"
3915
  msgstr ""
3916
 
3917
+ #: defaults.php:2052
3918
  msgid "Removed user %TargetUsername% from the site %SiteName%"
3919
  msgstr ""
3920
 
3921
+ #: defaults.php:2054
3922
  msgid "Site role"
3923
  msgstr ""
3924
 
3925
+ #: defaults.php:2065
3926
  msgid "New network user created"
3927
  msgstr ""
3928
 
3929
+ #: defaults.php:2066
3930
  msgid "Created the new network user %NewUserData->Username%."
3931
  msgstr ""
3932
 
3933
+ #: defaults.php:2078
3934
+ msgid "Network user has been activated"
3935
+ msgstr ""
3936
+
3937
+ #: defaults.php:2079
3938
+ msgid "User %NewUserData->Username% has been activated."
3939
+ msgstr ""
3940
+
3941
+ #: defaults.php:2092
3942
+ msgid "Network user has signed-up"
3943
+ msgstr ""
3944
+
3945
+ #: defaults.php:2093
3946
+ msgid "User with the email address %email_address% has signed up to the network."
3947
+ msgstr ""
3948
+
3949
+ #: defaults.php:2095
3950
+ msgid "Username"
3951
+ msgstr ""
3952
+
3953
+ #: defaults.php:2104
3954
  msgid "Plugins & Themes"
3955
  msgstr ""
3956
 
3957
+ #: defaults.php:2109
3958
  msgid "User installed a plugin"
3959
  msgstr ""
3960
 
3961
+ #: defaults.php:2110
3962
  msgid "Installed the plugin %Plugin->Name%."
3963
  msgstr ""
3964
 
3965
+ #: defaults.php:2112 defaults.php:2125 defaults.php:2138 defaults.php:2151
3966
+ #: defaults.php:2247 defaults.php:2260 defaults.php:2273 defaults.php:2312
3967
+ #: defaults.php:2325
3968
  msgid "Version"
3969
  msgstr ""
3970
 
3971
+ #: defaults.php:2113 defaults.php:2126 defaults.php:2139 defaults.php:2152
3972
+ #: defaults.php:2165 defaults.php:2208 defaults.php:2221 defaults.php:2248
3973
+ #: defaults.php:2261 defaults.php:2274 defaults.php:2287 defaults.php:2313
3974
+ #: defaults.php:2326
3975
  msgid "Install location"
3976
  msgstr ""
3977
 
3978
+ #: defaults.php:2122
3979
  msgid "User activated a WordPress plugin"
3980
  msgstr ""
3981
 
3982
+ #: defaults.php:2123
3983
  msgid "Activated the plugin %PluginData->Name%."
3984
  msgstr ""
3985
 
3986
+ #: defaults.php:2135
3987
  msgid "User deactivated a WordPress plugin"
3988
  msgstr ""
3989
 
3990
+ #: defaults.php:2136
3991
  msgid "Deactivated the plugin %PluginData->Name%."
3992
  msgstr ""
3993
 
3994
+ #: defaults.php:2148
3995
  msgid "User uninstalled a plugin"
3996
  msgstr ""
3997
 
3998
+ #: defaults.php:2149
3999
  msgid "Uninstalled the plugin %PluginData->Name%."
4000
  msgstr ""
4001
 
4002
+ #: defaults.php:2161
4003
  msgid "User upgraded a plugin"
4004
  msgstr ""
4005
 
4006
+ #: defaults.php:2162
4007
  msgid "Updated the plugin %PluginData->Name%."
4008
  msgstr ""
4009
 
4010
+ #: defaults.php:2164
4011
  msgid "Updated version"
4012
  msgstr ""
4013
 
4014
+ #: defaults.php:2174
4015
  msgid "A plugin created a post"
4016
  msgstr ""
4017
 
4018
+ #: defaults.php:2175
4019
  msgid "The plugin created the post %PostTitle%."
4020
  msgstr ""
4021
 
4022
+ #: defaults.php:2189
4023
  msgid "A plugin deleted a post"
4024
  msgstr ""
4025
 
4026
+ #: defaults.php:2190
4027
  msgid "A plugin deleted the post %PostTitle%."
4028
  msgstr ""
4029
 
4030
+ #: defaults.php:2205
4031
  msgid "Changed the Automatic updates setting for a plugin."
4032
  msgstr ""
4033
 
4034
+ #: defaults.php:2206
4035
  msgid "Changed the Automatic updates setting for the plugin %name%."
4036
  msgstr ""
4037
 
4038
+ #: defaults.php:2218
4039
  msgid "Changed the Automatic updates setting for a theme."
4040
  msgstr ""
4041
 
4042
+ #: defaults.php:2219
4043
  msgid "Changed the Automatic updates setting for the theme %name%."
4044
  msgstr ""
4045
 
4046
+ #: defaults.php:2231
4047
  msgid "User changed a file using the plugin editor"
4048
  msgstr ""
4049
 
4050
+ #: defaults.php:2232
4051
  msgid "Modified the file %File% with the plugin editor."
4052
  msgstr ""
4053
 
4054
+ #: defaults.php:2240
4055
  msgid "Themes"
4056
  msgstr ""
4057
 
4058
+ #: defaults.php:2244
4059
  msgid "User installed a theme"
4060
  msgstr ""
4061
 
4062
+ #: defaults.php:2245
4063
  msgid "Installed the theme %Theme->Name%."
4064
  msgstr ""
4065
 
4066
+ #: defaults.php:2257
4067
  msgid "User activated a theme"
4068
  msgstr ""
4069
 
4070
+ #: defaults.php:2258
4071
  msgid "Activated the theme %Theme->Name%."
4072
  msgstr ""
4073
 
4074
+ #: defaults.php:2270
4075
  msgid "User uninstalled a theme"
4076
  msgstr ""
4077
 
4078
+ #: defaults.php:2271
4079
  msgid "Deleted the theme %Theme->Name%."
4080
  msgstr ""
4081
 
4082
+ #: defaults.php:2283
4083
  msgid "User updated a theme"
4084
  msgstr ""
4085
 
4086
+ #: defaults.php:2284
4087
  msgid "Updated the theme %Theme->Name%."
4088
  msgstr ""
4089
 
4090
+ #: defaults.php:2286 defaults.php:2380
4091
  msgid "New version"
4092
  msgstr ""
4093
 
4094
+ #: defaults.php:2296
4095
  msgid "User changed a file using the theme editor"
4096
  msgstr ""
4097
 
4098
+ #: defaults.php:2297
4099
  msgid "Modified the file %Theme%/%File% with the theme editor."
4100
  msgstr ""
4101
 
4102
+ #: defaults.php:2305
4103
  msgid "Themes on Multisite"
4104
  msgstr ""
4105
 
4106
+ #: defaults.php:2309
4107
  msgid "Activated theme on network"
4108
  msgstr ""
4109
 
4110
+ #: defaults.php:2310
4111
  msgid "Network activated the theme %Theme->Name%."
4112
  msgstr ""
4113
 
4114
+ #: defaults.php:2322
4115
  msgid "Deactivated theme from network"
4116
  msgstr ""
4117
 
4118
+ #: defaults.php:2323
4119
  msgid "Network deactivated the theme %Theme->Name%."
4120
  msgstr ""
4121
 
4122
+ #: defaults.php:2335
4123
  msgid "WordPress & System"
4124
  msgstr ""
4125
 
4126
+ #: defaults.php:2340
4127
  msgid "Unknown Error"
4128
  msgstr ""
4129
 
4130
+ #: defaults.php:2341
4131
  msgid "An unexpected error has occurred."
4132
  msgstr ""
4133
 
4134
+ #: defaults.php:2346
4135
  msgid "PHP error"
4136
  msgstr ""
4137
 
4138
+ #: defaults.php:2347 defaults.php:2353 defaults.php:2359 defaults.php:2365
4139
+ #: defaults.php:2371
4140
  msgid "%Message%."
4141
  msgstr ""
4142
 
4143
+ #: defaults.php:2352
4144
  msgid "PHP warning"
4145
  msgstr ""
4146
 
4147
+ #: defaults.php:2358
4148
  msgid "PHP notice"
4149
  msgstr ""
4150
 
4151
+ #: defaults.php:2364
4152
  msgid "PHP exception"
4153
  msgstr ""
4154
 
4155
+ #: defaults.php:2370
4156
  msgid "PHP shutdown error"
4157
  msgstr ""
4158
 
4159
+ #: defaults.php:2376
4160
  msgid "WordPress was updated"
4161
  msgstr ""
4162
 
4163
+ #: defaults.php:2377
4164
  msgid "Updated WordPress."
4165
  msgstr ""
4166
 
4167
+ #: defaults.php:2379
4168
  msgid "Previous version"
4169
  msgstr ""
4170
 
4171
+ #: defaults.php:2396
4172
  msgid "Advertising Extensions"
4173
  msgstr ""
4174
 
4175
+ #: defaults.php:2397
4176
  msgid "%PromoName% %PromoMessage%"
4177
  msgstr ""
4178
 
4179
+ #: defaults.php:2401
4180
  msgid "Activity log plugin"
4181
  msgstr ""
4182
 
4183
+ #: defaults.php:2405
4184
  msgid "Events automatically pruned by system"
4185
  msgstr ""
4186
 
4187
+ #: defaults.php:2406
4188
  msgid "System automatically deleted %EventCount% events from the activity log."
4189
  msgstr ""
4190
 
4191
+ #: defaults.php:2415
4192
  msgid "Reset the plugin's settings to default"
4193
  msgstr ""
4194
 
4195
+ #: defaults.php:2416
4196
  msgid "Reset the activity log plugin's settings to default."
4197
  msgstr ""
4198
 
4199
+ #: defaults.php:2425
4200
  msgid "Purged the activity log"
4201
  msgstr ""
4202
 
4203
+ #: defaults.php:2426
4204
  msgid "Purged the activity log."
4205
  msgstr ""
4206
 
4207
+ #: defaults.php:2436
4208
  msgid "Deleted all the data about a user from the activity log."
4209
  msgstr ""
4210
 
4211
+ #: defaults.php:2437
4212
  msgid "Deleted all the data about the user <strong>%user%</strong> from the activity log."
4213
  msgstr ""
4214
 
4215
+ #: defaults.php:2450
4216
  msgid "Deleted all the data of a specific type from the activity log."
4217
  msgstr ""
4218
 
4219
+ #: defaults.php:2451
4220
  msgid "Deleted all the data about the %deleted_data_type% %deleted_data% from the activity log."
4221
  msgstr ""
4222
 
4223
+ #: defaults.php:2461
4224
  msgid "Some WP Activity Log plugin settings on this site were propagated and overridden from the MainWP dashboard"
4225
  msgstr ""
4226
 
4227
+ #: defaults.php:2462
4228
  msgid "Some <strong>WP Activity Log</strong> plugin settings on this site were propagated and overridden from the MainWP dashboard."
4229
  msgstr ""
4230
 
4231
+ #: defaults.php:2472
4232
  msgid "Changed the status of the Login Page Notification"
4233
  msgstr ""
4234
 
4235
+ #: defaults.php:2473
4236
  msgid "Changed the status of the <strong>Login Page Notification.</strong>"
4237
  msgstr ""
4238
 
4239
+ #: defaults.php:2482
4240
  msgid "Changed the text of the Login Page Notification"
4241
  msgstr ""
4242
 
4243
+ #: defaults.php:2483
4244
  msgid "Changed the text of the <strong>Login Page Notification.</strong>"
4245
  msgstr ""
4246
 
4247
+ #: defaults.php:2492
4248
  msgid "Changed the status of the Reverse proxy / firewall option"
4249
  msgstr ""
4250
 
4251
+ #: defaults.php:2493
4252
  msgid "Changed the status of the <strong>Reverse proxy / firewall option.</strong>"
4253
  msgstr ""
4254
 
4255
+ #: defaults.php:2502
4256
  msgid "Changed the Restrict plugin access setting"
4257
  msgstr ""
4258
 
4259
+ #: defaults.php:2503
4260
  msgid "Changed the <strong>Restrict plugin access</strong> setting to %new_setting%."
4261
  msgstr ""
4262
 
4263
+ #: defaults.php:2505 defaults.php:2539 defaults.php:3009 defaults.php:3096
4264
+ #: defaults.php:3109 defaults.php:3402
4265
  msgid "Previous setting"
4266
  msgstr ""
4267
 
4268
+ #: defaults.php:2514
4269
  msgid "The user %user% to / from the list of users who can view the activity log"
4270
  msgstr ""
4271
 
4272
+ #: defaults.php:2515
4273
  msgid "The user %user% to / from the list of users who can view the activity log."
4274
  msgstr ""
4275
 
4276
+ #: defaults.php:2517
4277
  msgid "Previous list of users who had access to view the activity log"
4278
  msgstr ""
4279
 
4280
+ #: defaults.php:2526
4281
  msgid "Changed the status of the Hide plugin in plugins page setting"
4282
  msgstr ""
4283
 
4284
+ #: defaults.php:2527
4285
  msgid "Changed the status of the <strong>Hide plugin in plugins page</strong> setting."
4286
  msgstr ""
4287
 
4288
+ #: defaults.php:2536
4289
  msgid "Changed the Activity log retention setting"
4290
  msgstr ""
4291
 
4292
+ #: defaults.php:2537
4293
  msgid "Changed the <strong>Activity log retention</strong> to %new_setting%."
4294
  msgstr ""
4295
 
4296
+ #: defaults.php:2548
4297
  msgid "A user was added to / from the list of excluded users from the activity log"
4298
  msgstr ""
4299
 
4300
+ #: defaults.php:2549
4301
  msgid "The user %user% to / from the list of excluded users from the activity log."
4302
  msgstr ""
4303
 
4304
+ #: defaults.php:2551 defaults.php:2563
4305
  msgid "Previous list of users"
4306
  msgstr ""
4307
 
4308
+ #: defaults.php:2560
4309
  msgid "A user role was added to / from the list of excluded roles from the activity log"
4310
  msgstr ""
4311
 
4312
+ #: defaults.php:2561
4313
  msgid "The user role %role% to / from the list of excluded roles from the activity log."
4314
  msgstr ""
4315
 
4316
+ #: defaults.php:2572
4317
  msgid "An IP address was added to / from the list of excluded IP addresses from the activity log"
4318
  msgstr ""
4319
 
4320
+ #: defaults.php:2573
4321
  msgid "The IP address %ip% to / from the list of excluded IP addresses from the activity log."
4322
  msgstr ""
4323
 
4324
+ #: defaults.php:2575
4325
  msgid "Previous list of IPs"
4326
  msgstr ""
4327
 
4328
+ #: defaults.php:2584
4329
  msgid "A post type was added to / from the list of excluded post types from the activity log"
4330
  msgstr ""
4331
 
4332
+ #: defaults.php:2585
4333
  msgid "The post type %post_type% to / from the list of excluded post types from the activity log."
4334
  msgstr ""
4335
 
4336
+ #: defaults.php:2587
4337
  msgid "Previous list of Post types"
4338
  msgstr ""
4339
 
4340
+ #: defaults.php:2596
4341
  msgid "A custom field was added to / from the list of excluded custom fields from the activity log"
4342
  msgstr ""
4343
 
4344
+ #: defaults.php:2597
4345
  msgid "The custom field %custom_field% to / from the list of excluded custom fields from the activity log."
4346
  msgstr ""
4347
 
4348
+ #: defaults.php:2599
4349
  msgid "Previous list of Custom fields"
4350
  msgstr ""
4351
 
4352
+ #: defaults.php:2608
4353
  msgid "A custom field was added to / from the list of excluded user profile custom fields from the activity log"
4354
  msgstr ""
4355
 
4356
+ #: defaults.php:2609
4357
  msgid "The custom field %custom_field% to / from the list of excluded user profile custom fields from the activity log."
4358
  msgstr ""
4359
 
4360
+ #: defaults.php:2611
4361
  msgid "Previous list of user profile Custom fields"
4362
  msgstr ""
4363
 
4364
+ #: defaults.php:2619
4365
  msgid "Notifications & Integrations"
4366
  msgstr ""
4367
 
4368
+ #: defaults.php:2623
4369
  msgid "Changed the status of the Daily Summary of Activity Log"
4370
  msgstr ""
4371
 
4372
+ #: defaults.php:2624
4373
  msgid "Changed the status of the <strong>Daily Summary of Activity Log.</strong>."
4374
  msgstr ""
4375
 
4376
+ #: defaults.php:2633
4377
  msgid "Modified the reciepients of the Daily Summary of Activity Log."
4378
  msgstr ""
4379
 
4380
+ #: defaults.php:2634
4381
  msgid "Modified the reciepients of the <strong>Daily Summary of Activity Log</strong>."
4382
  msgstr ""
4383
 
4384
+ #: defaults.php:2636
4385
  msgid "New recipient"
4386
  msgstr ""
4387
 
4388
+ #: defaults.php:2637
4389
  msgid "Previous recipient"
4390
  msgstr ""
4391
 
4392
+ #: defaults.php:2646
4393
  msgid "Changed the status of a built in notification"
4394
  msgstr ""
4395
 
4396
+ #: defaults.php:2647
4397
  msgid "Changed the status of the built in notification %notification_name%."
4398
  msgstr ""
4399
 
4400
+ #: defaults.php:2656
4401
  msgid "Modified the recipient(s) of the built a notification"
4402
  msgstr ""
4403
 
4404
+ #: defaults.php:2657
4405
  msgid "Modified the recipient(s) of the built in notification %notification_name%."
4406
  msgstr ""
4407
 
4408
+ #: defaults.php:2659
4409
  msgid "New recipient(s)"
4410
  msgstr ""
4411
 
4412
+ #: defaults.php:2660
4413
  msgid "Previous recipient(s)"
4414
  msgstr ""
4415
 
4416
+ #: defaults.php:2669
4417
  msgid "Added a new custom notification"
4418
  msgstr ""
4419
 
4420
+ #: defaults.php:2670
4421
  msgid "Added a new custom notification %notification_name%."
4422
  msgstr ""
4423
 
4424
+ #: defaults.php:2672 defaults.php:2684
4425
  msgid "Recipient(s)"
4426
  msgstr ""
4427
 
4428
+ #: defaults.php:2681
4429
  msgid "Modified a custom notification"
4430
  msgstr ""
4431
 
4432
+ #: defaults.php:2682
4433
  msgid "Modified the custom notification %notification_name%."
4434
  msgstr ""
4435
 
4436
+ #: defaults.php:2693
4437
  msgid "Changed the status of a custom notification"
4438
  msgstr ""
4439
 
4440
+ #: defaults.php:2694
4441
  msgid "Changed the status of the custom notification %notification_name%."
4442
  msgstr ""
4443
 
4444
+ #: defaults.php:2703
4445
  msgid "Deleted a custom notification"
4446
  msgstr ""
4447
 
4448
+ #: defaults.php:2704
4449
  msgid "Deleted the custom notification %notification_name%."
4450
  msgstr ""
4451
 
4452
+ #: defaults.php:2713
4453
  msgid "Modified a default notification template"
4454
  msgstr ""
4455
 
4456
+ #: defaults.php:2714
4457
  msgid "Modified the default %template_name% notification template."
4458
  msgstr ""
4459
 
4460
+ #: defaults.php:2725
4461
  msgid "Added a new integrations connection"
4462
  msgstr ""
4463
 
4464
+ #: defaults.php:2726
4465
  msgid "Added / removed the integrations connection %name%"
4466
  msgstr ""
4467
 
4468
+ #: defaults.php:2728 defaults.php:2740
4469
  msgid "Connection type"
4470
  msgstr ""
4471
 
4472
+ #: defaults.php:2737
4473
  msgid "Modified an integrations connection"
4474
  msgstr ""
4475
 
4476
+ #: defaults.php:2738
4477
  msgid "Modified the integrations connection %name%."
4478
  msgstr ""
4479
 
4480
+ #: defaults.php:2749
4481
  msgid "Deleted a integrations connection"
4482
  msgstr ""
4483
 
4484
+ #: defaults.php:2750
4485
  msgid "Deleted the integrations connection %name%."
4486
  msgstr ""
4487
 
4488
+ #: defaults.php:2759
4489
  msgid "Added a new activity log mirror"
4490
  msgstr ""
4491
 
4492
+ #: defaults.php:2760
4493
  msgid "Added a new activity log mirror %name%."
4494
  msgstr ""
4495
 
4496
+ #: defaults.php:2762 defaults.php:2774 defaults.php:2786
4497
  msgid "Connection used by this mirror"
4498
  msgstr ""
4499
 
4500
+ #: defaults.php:2771
4501
  msgid "Modified an activity log mirror"
4502
  msgstr ""
4503
 
4504
+ #: defaults.php:2772
4505
  msgid "Modified the activity log mirror %name%."
4506
  msgstr ""
4507
 
4508
+ #: defaults.php:2783
4509
  msgid "Changed the status of an activity log mirror"
4510
  msgstr ""
4511
 
4512
+ #: defaults.php:2784
4513
  msgid "Changed the status of the activity log mirror %name%."
4514
  msgstr ""
4515
 
4516
+ #: defaults.php:2795
4517
  msgid "Deleted an activity log mirror"
4518
  msgstr ""
4519
 
4520
+ #: defaults.php:2796
4521
  msgid "Deleted the activity log mirror %name%."
4522
  msgstr ""
4523
 
4524
+ #: defaults.php:2805
4525
  msgid "Changed the status of Logging of events to the database"
4526
  msgstr ""
4527
 
4528
+ #: defaults.php:2806
4529
  msgid "Changed the status of <strong>Logging of events to the database</strong>."
4530
  msgstr ""
4531
 
4532
+ #: defaults.php:2814
4533
  msgid "WordPress Site Settings"
4534
  msgstr ""
4535
 
4536
+ #: defaults.php:2818
4537
  msgid "Option Anyone Can Register in WordPress settings changed"
4538
  msgstr ""
4539
 
4540
+ #: defaults.php:2819
4541
  msgid "The <strong>Membership</strong> setting <strong>Anyone can register</strong>."
4542
  msgstr ""
4543
 
4544
+ #: defaults.php:2828
4545
  msgid "New User Default Role changed"
4546
  msgstr ""
4547
 
4548
+ #: defaults.php:2829
4549
  msgid "Changed the <strong>New user default role</strong> WordPress setting."
4550
  msgstr ""
4551
 
4552
+ #: defaults.php:2832
4553
  msgid "New role"
4554
  msgstr ""
4555
 
4556
+ #: defaults.php:2841
4557
  msgid "WordPress Administrator Notification email changed"
4558
  msgstr ""
4559
 
4560
+ #: defaults.php:2842
4561
  msgid "Change the <strong>Administrator email address</strong> in the WordPress settings."
4562
  msgstr ""
4563
 
4564
+ #: defaults.php:2844
4565
  msgid "Previous address"
4566
  msgstr ""
4567
 
4568
+ #: defaults.php:2845
4569
  msgid "New address"
4570
  msgstr ""
4571
 
4572
+ #: defaults.php:2854
4573
  msgid "User changes the WordPress Permalinks"
4574
  msgstr ""
4575
 
4576
+ #: defaults.php:2855
4577
  msgid "Changed the <strong>WordPress permalinks</strong>."
4578
  msgstr ""
4579
 
4580
+ #: defaults.php:2857
4581
  msgid "Previous permalinks"
4582
  msgstr ""
4583
 
4584
+ #: defaults.php:2858
4585
  msgid "New permalinks"
4586
  msgstr ""
4587
 
4588
+ #: defaults.php:2867
4589
  msgid "Enabled/Disabled the option Discourage search engines from indexing this site"
4590
  msgstr ""
4591
 
4592
+ #: defaults.php:2868
4593
  msgid "Changed the status of the WordPress setting <strong>Search engine visibility</strong> (Discourage search engines from indexing this site)"
4594
  msgstr ""
4595
 
4596
+ #: defaults.php:2877
4597
  msgid "Enabled/Disabled comments on all the website"
4598
  msgstr ""
4599
 
4600
+ #: defaults.php:2878
4601
  msgid "Changed the status of the WordPress setting <strong>Allow people to submit comments on new posts</strong>."
4602
  msgstr ""
4603
 
4604
+ #: defaults.php:2888
4605
  msgid "Enabled/Disabled the option Comment author must fill out name and email"
4606
  msgstr ""
4607
 
4608
+ #: defaults.php:2889
4609
  msgid "Changed the status of the WordPress setting <strong>.Comment author must fill out name and email</strong>."
4610
  msgstr ""
4611
 
4612
+ #: defaults.php:2898
4613
  msgid "Enabled/Disabled the option Users must be logged in and registered to comment"
4614
  msgstr ""
4615
 
4616
+ #: defaults.php:2899
4617
  msgid "Changed the status of the WordPress setting <strong>Users must be registered and logged in to comment</strong>."
4618
  msgstr ""
4619
 
4620
+ #: defaults.php:2908
4621
  msgid "Enabled/Disabled the option to automatically close comments"
4622
  msgstr ""
4623
 
4624
+ #: defaults.php:2909
4625
  msgid "Changed the status of the WordPress setting <strong>Automatically close comments after %Value% days</strong>."
4626
  msgstr ""
4627
 
4628
+ #: defaults.php:2918
4629
  msgid "Changed the value of the option Automatically close comments"
4630
  msgstr ""
4631
 
4632
+ #: defaults.php:2919
4633
  msgid "Changed the value of the WordPress setting <strong>Automatically close comments after a number of days</strong> to %NewValue%."
4634
  msgstr ""
4635
 
4636
+ #: defaults.php:2930
4637
  msgid "Enabled/Disabled the option for comments to be manually approved"
4638
  msgstr ""
4639
 
4640
+ #: defaults.php:2931
4641
  msgid "Changed the value of the WordPress setting <strong>Comments must be manualy approved</strong>."
4642
  msgstr ""
4643
 
4644
+ #: defaults.php:2940
4645
  msgid "Enabled/Disabled the option for an author to have previously approved comments for the comments to appear"
4646
  msgstr ""
4647
 
4648
+ #: defaults.php:2941
4649
  msgid "Changed the value of the WordPress setting <strong>Comment author must have a previously approved comment</strong>."
4650
  msgstr ""
4651
 
4652
+ #: defaults.php:2950
4653
  msgid "Changed the number of links that a comment must have to be held in the queue"
4654
  msgstr ""
4655
 
4656
+ #: defaults.php:2951
4657
  msgid "Changed the value of the WordPress setting <strong>Hold a comment in the queue if it contains links</strong> to %NewValue% links."
4658
  msgstr ""
4659
 
4660
+ #: defaults.php:2962
4661
  msgid "Modified the list of keywords for comments moderation"
4662
  msgstr ""
4663
 
4664
+ #: defaults.php:2963
4665
+ msgid "Modified the list of keywords for comments moderation in WordPress."
4666
  msgstr ""
4667
 
4668
+ #: defaults.php:2972
4669
  msgid "Modified the list of keywords for comments blacklisting"
4670
  msgstr ""
4671
 
4672
+ #: defaults.php:2973
4673
  msgid "Modified the list of <strong>Disallowed comment keys</strong> (keywords) for comments blacklisting in WordPress."
4674
  msgstr ""
4675
 
4676
+ #: defaults.php:2982
4677
  msgid "Option WordPress Address (URL) in WordPress settings changed"
4678
  msgstr ""
4679
 
4680
+ #: defaults.php:2983
4681
  msgid "Changed the <strong>WordPress address (URL)</strong> tp %new_url%."
4682
  msgstr ""
4683
 
4684
+ #: defaults.php:2994
4685
  msgid "Option Site Address (URL) in WordPress settings changed"
4686
  msgstr ""
4687
 
4688
+ #: defaults.php:2995
4689
  msgid "Changed the <strong>Site address (URL)</strong> to %new_url%."
4690
  msgstr ""
4691
 
4692
+ #: defaults.php:3006
4693
  msgid "Option Your homepage displays in WordPress settings changed"
4694
  msgstr ""
4695
 
4696
+ #: defaults.php:3007
4697
  msgid "Changed the <strong>Your homepage displays</strong> WordPress setting to %new_homepage%."
4698
  msgstr ""
4699
 
4700
+ #: defaults.php:3018
4701
  msgid "Option homepage in WordPress settings changed"
4702
  msgstr ""
4703
 
4704
+ #: defaults.php:3019
4705
  msgid "Changed the <strong>Homepage</strong> in the WordPress settings to %new_page%."
4706
  msgstr ""
4707
 
4708
+ #: defaults.php:3021 defaults.php:3033
4709
  msgid "Previous page"
4710
  msgstr ""
4711
 
4712
+ #: defaults.php:3030
4713
  msgid "Option posts page in WordPress settings changed"
4714
  msgstr ""
4715
 
4716
+ #: defaults.php:3031
4717
  msgid "Changed the <strong> Posts</strong> page in the WordPress settings to %new_page%."
4718
  msgstr ""
4719
 
4720
+ #: defaults.php:3043
4721
  msgid "Option Timezone in WordPress settings changed"
4722
  msgstr ""
4723
 
4724
+ #: defaults.php:3044
4725
  msgid "Changed the <strong>Timezone</strong> in the WordPress settings to %new_timezone%."
4726
  msgstr ""
4727
 
4728
+ #: defaults.php:3046
4729
  msgid "Previous timezone"
4730
  msgstr ""
4731
 
4732
+ #: defaults.php:3055
4733
  msgid "Option Date format in WordPress settings changed"
4734
  msgstr ""
4735
 
4736
+ #: defaults.php:3056
4737
  msgid "Changed the <strong>Date format</strong> in the WordPress settings to %new_date_format%."
4738
  msgstr ""
4739
 
4740
+ #: defaults.php:3058 defaults.php:3070
4741
  msgid "Previous format"
4742
  msgstr ""
4743
 
4744
+ #: defaults.php:3067
4745
  msgid "Option Time format in WordPress settings changed"
4746
  msgstr ""
4747
 
4748
+ #: defaults.php:3068
4749
  msgid "Changed the <strong>Time format</strong> in the WordPress settings to %new_time_format%."
4750
  msgstr ""
4751
 
4752
+ #: defaults.php:3080
4753
  msgid "Option Automatic updates setting changed"
4754
  msgstr ""
4755
 
4756
+ #: defaults.php:3081
4757
  msgid "Changed the <strong>Automatic updates</strong> setting."
4758
  msgstr ""
4759
 
4760
+ #: defaults.php:3083
4761
  msgid "New setting status"
4762
  msgstr ""
4763
 
4764
+ #: defaults.php:3093
4765
  msgid "Option Site Language setting changed"
4766
  msgstr ""
4767
 
4768
+ #: defaults.php:3094
4769
  msgid "Changed the <strong>Site Language</strong> to %new_value%."
4770
  msgstr ""
4771
 
4772
+ #: defaults.php:3106
4773
+ msgid "Option Site Title changed"
4774
+ msgstr ""
4775
+
4776
+ #: defaults.php:3107
4777
+ msgid "Changed the <strong>Site Title</strong> to %new_value%."
4778
+ msgstr ""
4779
+
4780
+ #: defaults.php:3117
4781
  msgid "Database Events"
4782
  msgstr ""
4783
 
4784
+ #: defaults.php:3121
4785
  msgid "Plugin created table"
4786
  msgstr ""
4787
 
4788
+ #: defaults.php:3122
4789
  msgid "The plugin %Plugin->Name% created this table in the database."
4790
  msgstr ""
4791
 
4792
+ #: defaults.php:3124 defaults.php:3136 defaults.php:3148 defaults.php:3160
4793
+ #: defaults.php:3172
4794
  msgid "Table"
4795
  msgstr ""
4796
 
4797
+ #: defaults.php:3133
4798
  msgid "Plugin modified table structure"
4799
  msgstr ""
4800
 
4801
+ #: defaults.php:3134
4802
  msgid "The plugin %Plugin->Name% modified the structure of a database table."
4803
  msgstr ""
4804
 
4805
+ #: defaults.php:3145
4806
  msgid "Plugin deleted table"
4807
  msgstr ""
4808
 
4809
+ #: defaults.php:3146
4810
  msgid "The plugin %Plugin->Name% deleted this table from the database."
4811
  msgstr ""
4812
 
4813
+ #: defaults.php:3157
4814
  msgid "Theme created tables"
4815
  msgstr ""
4816
 
4817
+ #: defaults.php:3158
4818
  msgid "The theme %Theme->Name% created this tables in the database."
4819
  msgstr ""
4820
 
4821
+ #: defaults.php:3169
4822
  msgid "Theme modified tables structure"
4823
  msgstr ""
4824
 
4825
+ #: defaults.php:3170
4826
  msgid "The theme %Theme->Name% modified the structure of this database table"
4827
  msgstr ""
4828
 
4829
+ #: defaults.php:3181
4830
  msgid "Theme deleted tables"
4831
  msgstr ""
4832
 
4833
+ #: defaults.php:3182
4834
  msgid "The theme %Theme->Name% deleted this table from the database."
4835
  msgstr ""
4836
 
4837
+ #: defaults.php:3184 defaults.php:3196 defaults.php:3208 defaults.php:3220
4838
+ #: defaults.php:3232 defaults.php:3244 defaults.php:3256
4839
  msgid "Tables"
4840
  msgstr ""
4841
 
4842
+ #: defaults.php:3193
4843
  msgid "Unknown component created tables"
4844
  msgstr ""
4845
 
4846
+ #: defaults.php:3194
4847
  msgid "An unknown component created these tables in the database."
4848
  msgstr ""
4849
 
4850
+ #: defaults.php:3205
4851
  msgid "Unknown component modified tables structure"
4852
  msgstr ""
4853
 
4854
+ #: defaults.php:3206
4855
  msgid "An unknown component modified the structure of these database tables."
4856
  msgstr ""
4857
 
4858
+ #: defaults.php:3217
4859
  msgid "Unknown component deleted tables"
4860
  msgstr ""
4861
 
4862
+ #: defaults.php:3218
4863
  msgid "An unknown component deleted these tables from the database."
4864
  msgstr ""
4865
 
4866
+ #: defaults.php:3229
4867
  msgid "WordPress created tables"
4868
  msgstr ""
4869
 
4870
+ #: defaults.php:3230
4871
  msgid "WordPress has created these tables in the database."
4872
  msgstr ""
4873
 
4874
+ #: defaults.php:3241
4875
  msgid "WordPress modified tables structure"
4876
  msgstr ""
4877
 
4878
+ #: defaults.php:3242
4879
  msgid "WordPress modified the structure of these database tables."
4880
  msgstr ""
4881
 
4882
+ #: defaults.php:3253
4883
  msgid "WordPress deleted tables"
4884
  msgstr ""
4885
 
4886
+ #: defaults.php:3254
4887
  msgid "WordPress deleted these tables from the database."
4888
  msgstr ""
4889
 
4890
+ #: defaults.php:3265
4891
  msgid "Multisite Network Sites"
4892
  msgstr ""
4893
 
4894
+ #: defaults.php:3266
4895
+ msgid "MultiSite"
4896
+ msgstr ""
4897
+
4898
+ #: defaults.php:3270
4899
  msgid "New site added on the network"
4900
  msgstr ""
4901
 
4902
+ #: defaults.php:3271
4903
  msgid "Added the new site %SiteName% to the network."
4904
  msgstr ""
4905
 
4906
+ #: defaults.php:3282
4907
  msgid "Existing site archived"
4908
  msgstr ""
4909
 
4910
+ #: defaults.php:3283
4911
  msgid "Archived the site %SiteName% on the network."
4912
  msgstr ""
4913
 
4914
+ #: defaults.php:3294
4915
  msgid "Archived site has been unarchived"
4916
  msgstr ""
4917
 
4918
+ #: defaults.php:3295
4919
  msgid "Unarchived the site %SiteName%."
4920
  msgstr ""
4921
 
4922
+ #: defaults.php:3306
4923
  msgid "Deactivated site has been activated"
4924
  msgstr ""
4925
 
4926
+ #: defaults.php:3307
4927
  msgid "Activated the site %SiteName% on the network."
4928
  msgstr ""
4929
 
4930
+ #: defaults.php:3318
4931
  msgid "Site has been deactivated"
4932
  msgstr ""
4933
 
4934
+ #: defaults.php:3319
4935
  msgid "Deactiveated the site %SiteName% on the network."
4936
  msgstr ""
4937
 
4938
+ #: defaults.php:3330
4939
  msgid "Existing site deleted from network"
4940
  msgstr ""
4941
 
4942
+ #: defaults.php:3331
4943
  msgid "The site: %SiteName%."
4944
  msgstr ""
4945
 
4946
+ #: defaults.php:3342
4947
  msgid "Allow site administrators to add new users to their sites settings changed"
4948
  msgstr ""
4949
 
4950
+ #: defaults.php:3343
4951
  msgid "Changed the status of the network setting <strong>Allow site administrators to add new users to their sites</strong>."
4952
  msgstr ""
4953
 
4954
+ #: defaults.php:3352
4955
  msgid "Site upload space settings changed"
4956
  msgstr ""
4957
 
4958
+ #: defaults.php:3353
4959
  msgid "Changed the status of the network setting <strong>Site upload space</strong> (to limit space allocated for each site's upload directory)."
4960
  msgstr ""
4961
 
4962
+ #: defaults.php:3362
4963
  msgid "Site upload space file size settings changed"
4964
  msgstr ""
4965
 
4966
+ #: defaults.php:3363
4967
  msgid "Changed the file size in the <strong>Site upload space</strong> network setting to %new_value%."
4968
  msgstr ""
4969
 
4970
+ #: defaults.php:3365
4971
  msgid "Previous size (MB)"
4972
  msgstr ""
4973
 
4974
+ #: defaults.php:3374
4975
  msgid "Site Upload file types settings changed"
4976
  msgstr ""
4977
 
4978
+ #: defaults.php:3375
4979
  msgid "Changed the network setting <strong>Upload file types (list of allowed file types)</strong>."
4980
  msgstr ""
4981
 
4982
+ #: defaults.php:3387
4983
  msgid "Site Max upload file size settings changed"
4984
  msgstr ""
4985
 
4986
+ #: defaults.php:3388
4987
  msgid "Changed the <strong>Max upload file size</strong> network setting to %new_value%."
4988
  msgstr ""
4989
 
4990
+ #: defaults.php:3390
4991
  msgid "Previous size (KB)"
4992
  msgstr ""
4993
 
4994
+ #: defaults.php:3399
4995
  msgid "Allow new registrations settings changed"
4996
  msgstr ""
4997
 
4998
+ #: defaults.php:3400
4999
  msgid "Changed the <strong>Allow new registrations</strong> setting to %new_setting%."
5000
  msgstr ""
5001
 
5002
+ #: defaults.php:3416
5003
+ msgid "File Changes"
5004
+ msgstr ""
5005
+
5006
+ #: defaults.php:3417
5007
+ msgid "Monitor File Changes"
5008
+ msgstr ""
5009
+
5010
+ #: defaults.php:3421
5011
  msgid "Dummy"
5012
  msgstr ""
5013
 
5014
+ #: third-party/vendor/deliciousbrains/wp-background-processing/classes/wp-background-process.php:371
5015
+ #, php-format
5016
+ msgid "Every %d Minutes"
5017
+ msgstr ""
5018
+
5019
+ #: wp-security-audit-log.php:861 wp-security-audit-log.php:889
5020
  #, php-format
5021
  msgid "Hey %s"
5022
  msgstr ""
5023
 
5024
+ #: wp-security-audit-log.php:865
5025
  msgid "Never miss an important update! Opt-in to our security and feature updates notifications, and non-sensitive diagnostic tracking with freemius.com."
5026
  msgstr ""
5027
 
5028
+ #: wp-security-audit-log.php:866 wp-security-audit-log.php:901
5029
  msgid "Note: "
5030
  msgstr ""
5031
 
5032
+ #: wp-security-audit-log.php:867 wp-security-audit-log.php:902
5033
  msgid "NO ACTIVITY LOG ACTIVITY & DATA IS SENT BACK TO OUR SERVERS."
5034
  msgstr ""
5035
 
5036
+ #: wp-security-audit-log.php:895
5037
  #, php-format
5038
  msgid "Please help us improve %1$s! If you opt-in, some non-sensitive data about your usage of %2$s will be sent to %3$s, a diagnostic tracking service we use. If you skip this, that's okay! %2$s will still work just fine."
5039
  msgstr ""
5040
 
5041
+ #: wp-security-audit-log.php:947
5042
  #, php-format
5043
  msgid "You need to activate the licence key to use WP Activity Log Premium. %2$s"
5044
  msgstr ""
5045
 
5046
+ #: wp-security-audit-log.php:948
5047
  msgid "Activate the licence key now"
5048
  msgstr ""
5049
 
5050
+ #: wp-security-audit-log.php:972
5051
  #, php-format
5052
  msgid "%s You need to renew your license to continue using premium features."
5053
  msgstr ""
5054
 
5055
+ #: wp-security-audit-log.php:975
5056
  #, php-format
5057
  msgid "The license is limited to %s sub-sites. You need to upgrade your license to cover all the sub-sites on this network."
5058
  msgstr ""
5059
 
5060
+ #: wp-security-audit-log.php:1077
5061
  msgid "Error: You do not have sufficient permissions to disable this custom field."
5062
  msgstr ""
5063
 
5064
+ #: wp-security-audit-log.php:1125
5065
  #, php-format
5066
  msgid "Custom field %s is no longer being monitored."
5067
  msgstr ""
5068
 
5069
+ #: wp-security-audit-log.php:1130
5070
  #, php-format
5071
  msgid "Enable the monitoring of this custom field again from the %s tab in the plugin settings."
5072
  msgstr ""
5073
 
5074
+ #: wp-security-audit-log.php:1131
5075
  msgid "Excluded Objects"
5076
  msgstr ""
5077
 
5078
+ #: wp-security-audit-log.php:1144
5079
  msgid "Error: You do not have sufficient permissions to disable this alert."
5080
  msgstr ""
5081
 
5082
+ #: wp-security-audit-log.php:1168
5083
  #, php-format
5084
  msgid "Alert %1$s is no longer being monitored.<br /> %2$s"
5085
  msgstr ""
5086
 
5087
+ #: wp-security-audit-log.php:1168
5088
  msgid "You can enable this alert again from the Enable/Disable Alerts node in the plugin menu."
5089
  msgstr ""
5090
 
5091
+ #: wp-security-audit-log.php:1249
5092
  #, php-format
5093
  msgid "You are using a version of PHP that is older than %s, which is no longer supported."
5094
  msgstr ""
5095
 
5096
+ #: wp-security-audit-log.php:1251
5097
  msgid "Contact us on <a href=\"mailto:plugins@wpwhitesecurity.com\">plugins@wpwhitesecurity.com</a> to help you switch the version of PHP you are using."
5098
  msgstr ""
5099
 
5100
+ #: wp-security-audit-log.php:1256
5101
  #, php-format
5102
  msgid "Please install the %s plugin on the MainWP dashboard."
5103
  msgstr ""
5104
 
5105
+ #: wp-security-audit-log.php:1256
5106
  msgid "Activity Log for MainWP"
5107
  msgstr ""
5108
 
5109
+ #: wp-security-audit-log.php:1258
5110
  #, php-format
5111
  msgid "The WP Activity Log should be installed on the child sites only. Refer to the %s for more information."
5112
  msgstr ""
5113
 
5114
+ #: wp-security-audit-log.php:1258
5115
  msgid "getting started guide"
5116
  msgstr ""
5117
 
5118
+ #: wp-security-audit-log.php:1771
5119
  msgid "For security and auditing purposes, a record of all of your logged-in actions and changes within the WordPress dashboard will be recorded in an activity log with the <a href=\"https://wpactivitylog.com/\" target=\"_blank\">WP Activity Log plugin</a>. The audit log also includes the IP address where you accessed this site from."
5120
  msgstr ""
5121
 
5122
+ #: wp-security-audit-log.php:1790
5123
  msgid "Every 6 hours"
5124
  msgstr ""
5125
 
5126
+ #: wp-security-audit-log.php:1794
5127
  msgid "Every 45 minutes"
5128
  msgstr ""
5129
 
5130
+ #: wp-security-audit-log.php:1798
5131
  msgid "Every 30 minutes"
5132
  msgstr ""
5133
 
5134
+ #: wp-security-audit-log.php:1802
5135
  msgid "Every 15 minutes"
5136
  msgstr ""
5137
 
5138
+ #: wp-security-audit-log.php:1806
5139
  msgid "Every 10 minutes"
5140
  msgstr ""
5141
 
5142
+ #: wp-security-audit-log.php:1810
5143
  msgid "Every 1 minute"
5144
  msgstr ""
5145
 
5146
+ #: wp-security-audit-log.php:1824
5147
  #, php-format
5148
  msgid "Method %1$s is deprecated since version %2$s!"
5149
  msgstr ""
license.txt ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ <one line to give the program's name and a brief idea of what it does.>
635
+ Copyright (C) <year> <name of author>
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ <program> Copyright (C) <year> <name of author>
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get your employer (if you work as a programmer) or school,
665
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
666
+ For more information on this, and how to apply and follow the GNU GPL, see
667
+ <https://www.gnu.org/licenses/>.
668
+
669
+ The GNU General Public License does not permit incorporating your program
670
+ into proprietary programs. If your program is a subroutine library, you
671
+ may consider it more useful to permit linking proprietary applications with
672
+ the library. If this is what you want to do, use the GNU Lesser General
673
+ Public License instead of this License. But first, please read
674
+ <https://www.gnu.org/licenses/why-not-lgpl.html>.
readme.txt CHANGED
@@ -5,8 +5,8 @@ License: GPLv3
5
  License URI: https://www.gnu.org/licenses/gpl.html
6
  Tags: activity log, wordpress activity logs, security audit log, audit log, user tracking, security event log, audit trail, wordpress security monitor, wordpress admin, wordpress admin monitoring, user activity, admin, multisite, SMS alerts, wordpress monitoring, email notification, wordpress email alerts, tracking, user tracking, user activity report, wordpress audit trail
7
  Requires at least: 5.0
8
- Tested up to: 5.9
9
- Stable tag: 4.3.6
10
  Requires PHP: 7.0
11
 
12
  The #1 user-rated activity log plugin. Keep a comprehensive log of the changes that happen on your site with this easy to use plugin.
@@ -162,7 +162,7 @@ WP Activity Log can keep also a detailed log of changes that happen on other plu
162
  * [Why WP Activity Log](https://wpactivitylog.com/why-wp-activity-log/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
163
  * [WordPress Multisite Features](https://wpactivitylog.com/support/kb/activity-log-multisite-network-features/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
164
  * [The WP Activity Log plugin website](https://wpactivitylog.com/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
165
- * [Activity logs for MainWP](https://wpactivitylog.com/extensions/mainwp-activity-log/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
166
 
167
  #### WP Activity Log in your language!
168
  We need help translating the plugin and the activity log events. Please visit the [WordPress Translate Project](https://translate.wordpress.org/projects/wp-plugins/wp-security-audit-log/) to translate the plugin. Drop us an email on support@wpwhitesecurity.com to get mentioned in the list of translators below.
@@ -208,11 +208,52 @@ Please refer to our [support pages](https://wpactivitylog.com/support/?utm_sourc
208
 
209
  == Changelog ==
210
 
211
- = 4.3.6 (2022-02-15) =
212
- Release notes: the improvements in this update are required to prepare for WP Activity Log 4.4. Therefore it is important to install this update before you update to version 4.4.
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
  * **Improvements**
215
- * Removed opcache purging.
216
- * Improved error handling during plugin upgrade.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
 
218
  Refer to the [complete plugin changelog](https://wpactivitylog.com/support/kb/plugin-changelog/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description) for more detailed information about what was new, improved and fixed in previous versions of the WP Activity Log plugin.
5
  License URI: https://www.gnu.org/licenses/gpl.html
6
  Tags: activity log, wordpress activity logs, security audit log, audit log, user tracking, security event log, audit trail, wordpress security monitor, wordpress admin, wordpress admin monitoring, user activity, admin, multisite, SMS alerts, wordpress monitoring, email notification, wordpress email alerts, tracking, user tracking, user activity report, wordpress audit trail
7
  Requires at least: 5.0
8
+ Tested up to: 5.9.1
9
+ Stable tag: 4.4.0
10
  Requires PHP: 7.0
11
 
12
  The #1 user-rated activity log plugin. Keep a comprehensive log of the changes that happen on your site with this easy to use plugin.
162
  * [Why WP Activity Log](https://wpactivitylog.com/why-wp-activity-log/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
163
  * [WordPress Multisite Features](https://wpactivitylog.com/support/kb/activity-log-multisite-network-features/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
164
  * [The WP Activity Log plugin website](https://wpactivitylog.com/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
165
+ * [The definitive WordPress security guide](hhttps://www.wpwhitesecurity.com/wordpress-security/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description)
166
 
167
  #### WP Activity Log in your language!
168
  We need help translating the plugin and the activity log events. Please visit the [WordPress Translate Project](https://translate.wordpress.org/projects/wp-plugins/wp-security-audit-log/) to translate the plugin. Drop us an email on support@wpwhitesecurity.com to get mentioned in the list of translators below.
208
 
209
  == Changelog ==
210
 
211
+ = 4.4.0 (2022-02-08) =
212
+
213
+ Release notes: [New Reports engine with more criteria, reports management & more](https://wpactivitylog.com/wsal-4-4/)
214
+
215
+ * **New activity log event IDs**
216
+ * ID 6059: Changed the site's title.
217
+ * ID 4021: Changed the website URL in the user profile.
218
+ * ID 4013: User has been activated on a multisite network.
219
+
220
+ * **New features & functionality**
221
+ * Hooks to allow users to change the columns in reports or ad value from non-default columns. Refer to the [list of hooks in WP Activity Log](https://wpactivitylog.com/support/kb/list-hooks/) for more information.
222
+ * New UI for "Enable/Disable event IDs" with search and filtering functionality.
223
+ * New filter that allows user to add metadata to user information popup. Refer to the [List of hooks in WP Activity Log](https://wpactivitylog.com/support/kb/list-hooks/) for more information.
224
+ * The new [Activity Log for TablePress extension](https://wpactivitylog.com/extensions/tablepress-activity-log/).
225
 
226
  * **Improvements**
227
+ * Changed the database schema for improved storing of data, and faster writing and reading. After the upgrade the plugin will launch the upgrade process which might take some time to complete, depending on the amount of data in the activity log.
228
+ * Improved the coverage of changes done to a website via REST API.
229
+ * Removed obsolete code used for advertorial events in the activity log viewer.
230
+ * All plugin settings now have the wsal_ prefix automatically added to them.
231
+ * Rewritten some of the settings help text in the plugin to better explain the settings.
232
+ * Removed obsolete settings & code of the old file integrity scanner (now part of [Website File Changes Monitor plugin](https://www.wpwhitesecurity.com/wordpress-plugins/website-file-changes-monitor/)).
233
+ * Removed obsolete reference to the old file changes scanner in the daily summary email.
234
+ * Made a number of JS strings available for translation.
235
+ * Removed a number of plugin settings from autoload for improved performance.
236
+ * Improved the plugin's metadata and added the licensing information.
237
+ * Long URL strings in activity log events are now automatically truncated. Full URL can be seen with just a click.
238
+ * Removed forced database table collation: plugin now uses the default WordPress table collation.
239
+ * Updated the "Help & Contact Us" page; improved text and added more relevant information.
240
+ * Improved several UI sections in the Third Party Connections module.
241
+ * Improved the check for writing activity log to external database; now it is less restrictive and faster.
242
+
243
+ * **Security fix**
244
+ * Upgraded the Freemius SDK to version 2.4.3.
245
+
246
+ * **Bug fixes**
247
+ * Fixed: Database error when trying to log in with a non-existing user and a login notification is enabled.
248
+ * Fixed: In some edge cases the plugin was creating an empty "external database" connection string.
249
+ * Fixed a number of typos in the text of activity log events.
250
+ * Fixed: Auto complete in the Delete activity log data section was not returning the correct list of objects.
251
+ * Fixed: Wrong object reported for event ID 5029.
252
+ * Fixed: Event ID 4000 not reported when front-end sensor is disabled.
253
+ * Fixed: "Unknown connection type" reported back setting up a third party connection on specific versions of WordPress.
254
+ * Fixed: Event ID 6320 (added / removed connection) reported instead of event ID 6321 (modified connection).
255
+ * Fixed: Function that was running on "add_filter" instead of "add_action" - [Support ticket](https://wordpress.org/support/topic/issue-with-hook-name-and-param-callback/).
256
+ * Fixed: PHP warning about OPCacheUtils.php in specific setups.
257
+ * Fixed: Edge case in which other plugins couldn't be installed or updated when WP Activity Log was activated.
258
 
259
  Refer to the [complete plugin changelog](https://wpactivitylog.com/support/kb/plugin-changelog/?utm_source=wordpress.org&utm_medium=referral&utm_campaign=WSAL&utm_content=plugin+repos+description) for more detailed information about what was new, improved and fixed in previous versions of the WP Activity Log plugin.
third-party/freemius/wordpress-sdk/includes/class-freemius.php CHANGED
@@ -3550,6 +3550,8 @@
3550
  * @since 1.1.7.3
3551
  */
3552
  static function _toggle_debug_mode() {
 
 
3553
  if ( ! is_super_admin() ) {
3554
  return;
3555
  }
@@ -3571,10 +3573,19 @@
3571
  * @since 1.2.1.6
3572
  */
3573
  static function _get_debug_log() {
 
 
 
 
 
 
 
 
 
3574
  $logs = FS_Logger::load_db_logs(
3575
  fs_request_get( 'filters', false, 'post' ),
3576
- ! empty( $_POST['limit'] ) && is_numeric( $_POST['limit'] ) ? $_POST['limit'] : 200,
3577
- ! empty( $_POST['offset'] ) && is_numeric( $_POST['offset'] ) ? $_POST['offset'] : 0
3578
  );
3579
 
3580
  self::shoot_ajax_success( $logs );
@@ -4447,6 +4458,12 @@
4447
  * @since 1.0.9
4448
  */
4449
  function _email_about_firewall_issue() {
 
 
 
 
 
 
4450
  $this->_admin_notices->remove_sticky( 'failed_connect_api' );
4451
 
4452
  $pong = $this->ping();
@@ -4521,6 +4538,12 @@
4521
  * @since 1.1.7.4
4522
  */
4523
  function _retry_connectivity_test() {
 
 
 
 
 
 
4524
  $this->_admin_notices->remove_sticky( 'failed_connect_api_first' );
4525
 
4526
  $pong = $this->ping();
3550
  * @since 1.1.7.3
3551
  */
3552
  static function _toggle_debug_mode() {
3553
+ check_admin_referer( 'fs_toggle_debug_mode' );
3554
+
3555
  if ( ! is_super_admin() ) {
3556
  return;
3557
  }
3573
  * @since 1.2.1.6
3574
  */
3575
  static function _get_debug_log() {
3576
+ check_admin_referer( 'fs_get_debug_log' );
3577
+
3578
+ if ( ! is_super_admin() ) {
3579
+ return;
3580
+ }
3581
+
3582
+ $limit = min( ! empty( $_POST['limit'] ) ? absint( $_POST['limit'] ) : 200, 200 );
3583
+ $offset = min( ! empty( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 200, 200 );
3584
+
3585
  $logs = FS_Logger::load_db_logs(
3586
  fs_request_get( 'filters', false, 'post' ),
3587
+ $limit,
3588
+ $offset
3589
  );
3590
 
3591
  self::shoot_ajax_success( $logs );
4458
  * @since 1.0.9
4459
  */
4460
  function _email_about_firewall_issue() {
4461
+ check_admin_referer( 'fs_resolve_firewall_issues' );
4462
+
4463
+ if ( ! current_user_can( is_multisite() ? 'manage_options' : 'activate_plugins' ) ) {
4464
+ return;
4465
+ }
4466
+
4467
  $this->_admin_notices->remove_sticky( 'failed_connect_api' );
4468
 
4469
  $pong = $this->ping();
4538
  * @since 1.1.7.4
4539
  */
4540
  function _retry_connectivity_test() {
4541
+ check_admin_referer( 'fs_retry_connectivity_test' );
4542
+
4543
+ if ( ! current_user_can( is_multisite() ? 'manage_options' : 'activate_plugins' ) ) {
4544
+ return;
4545
+ }
4546
+
4547
  $this->_admin_notices->remove_sticky( 'failed_connect_api_first' );
4548
 
4549
  $pong = $this->ping();
third-party/freemius/wordpress-sdk/includes/managers/class-fs-admin-notice-manager.php CHANGED
@@ -175,7 +175,12 @@
175
  *
176
  */
177
  function dismiss_notice_ajax_callback() {
178
- $this->_sticky_storage->remove( $_POST['message_id'] );
 
 
 
 
 
179
  wp_die();
180
  }
181
 
@@ -469,4 +474,4 @@
469
  }
470
 
471
  #endregion
472
- }
175
  *
176
  */
177
  function dismiss_notice_ajax_callback() {
178
+ check_admin_referer( 'fs_dismiss_notice_action' );
179
+
180
+ if ( ! is_numeric( $_POST['message_id'] ) ) {
181
+ $this->_sticky_storage->remove( $_POST['message_id'] );
182
+ }
183
+
184
  wp_die();
185
  }
186
 
474
  }
475
 
476
  #endregion
477
+ }
third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/ArgumentNotExistException.php CHANGED
@@ -1,4 +1,8 @@
1
  <?php
 
 
 
 
2
  if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
3
  exit;
4
  }
@@ -6,4 +10,4 @@
6
  if ( ! class_exists( 'Freemius_ArgumentNotExistException' ) ) {
7
  class Freemius_ArgumentNotExistException extends Freemius_InvalidArgumentException {
8
  }
9
- }
1
  <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
  if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
7
  exit;
8
  }
10
  if ( ! class_exists( 'Freemius_ArgumentNotExistException' ) ) {
11
  class Freemius_ArgumentNotExistException extends Freemius_InvalidArgumentException {
12
  }
13
+ }
third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/EmptyArgumentException.php CHANGED
@@ -1,4 +1,8 @@
1
  <?php
 
 
 
 
2
  if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
3
  exit;
4
  }
@@ -6,4 +10,4 @@
6
  if ( ! class_exists( 'Freemius_EmptyArgumentException' ) ) {
7
  class Freemius_EmptyArgumentException extends Freemius_InvalidArgumentException {
8
  }
9
- }
1
  <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
  if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
7
  exit;
8
  }
10
  if ( ! class_exists( 'Freemius_EmptyArgumentException' ) ) {
11
  class Freemius_EmptyArgumentException extends Freemius_InvalidArgumentException {
12
  }
13
+ }
third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/Exception.php CHANGED
@@ -1,4 +1,8 @@
1
  <?php
 
 
 
 
2
  if ( ! class_exists( 'Freemius_Exception' ) ) {
3
  /**
4
  * Thrown when an API call returns an exception.
@@ -71,4 +75,4 @@
71
  return $str . $this->getMessage();
72
  }
73
  }
74
- }
1
  <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
  if ( ! class_exists( 'Freemius_Exception' ) ) {
7
  /**
8
  * Thrown when an API call returns an exception.
75
  return $str . $this->getMessage();
76
  }
77
  }
78
+ }
third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/InvalidArgumentException.php CHANGED
@@ -1,8 +1,12 @@
1
  <?php
 
 
 
 
2
  if ( ! class_exists( 'Freemius_Exception' ) ) {
3
  exit;
4
  }
5
 
6
  if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
7
  class Freemius_InvalidArgumentException extends Freemius_Exception { }
8
- }
1
  <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
  if ( ! class_exists( 'Freemius_Exception' ) ) {
7
  exit;
8
  }
9
 
10
  if ( ! class_exists( 'Freemius_InvalidArgumentException' ) ) {
11
  class Freemius_InvalidArgumentException extends Freemius_Exception { }
12
+ }
third-party/freemius/wordpress-sdk/includes/sdk/Exceptions/OAuthException.php CHANGED
@@ -1,4 +1,8 @@
1
  <?php
 
 
 
 
2
  if ( ! class_exists( 'Freemius_Exception' ) ) {
3
  exit;
4
  }
@@ -9,4 +13,4 @@
9
  parent::__construct( $pResult );
10
  }
11
  }
12
- }
1
  <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
  if ( ! class_exists( 'Freemius_Exception' ) ) {
7
  exit;
8
  }
13
  parent::__construct( $pResult );
14
  }
15
  }
16
+ }
third-party/freemius/wordpress-sdk/includes/sdk/FreemiusBase.php CHANGED
@@ -15,6 +15,10 @@
15
  * under the License.
16
  */
17
 
 
 
 
 
18
  if ( ! defined( 'FS_API__VERSION' ) ) {
19
  define( 'FS_API__VERSION', '1' );
20
  }
15
  * under the License.
16
  */
17
 
18
+ if ( ! defined( 'ABSPATH' ) ) {
19
+ exit;
20
+ }
21
+
22
  if ( ! defined( 'FS_API__VERSION' ) ) {
23
  define( 'FS_API__VERSION', '1' );
24
  }
third-party/freemius/wordpress-sdk/includes/sdk/FreemiusWordPress.php CHANGED
@@ -14,6 +14,9 @@
14
  * License for the specific language governing permissions and limitations
15
  * under the License.
16
  */
 
 
 
17
 
18
  require_once dirname( __FILE__ ) . '/FreemiusBase.php';
19
 
@@ -709,4 +712,4 @@
709
  }
710
 
711
  #endregion
712
- }
14
  * License for the specific language governing permissions and limitations
15
  * under the License.
16
  */
17
+ if ( ! defined( 'ABSPATH' ) ) {
18
+ exit;
19
+ }
20
 
21
  require_once dirname( __FILE__ ) . '/FreemiusBase.php';
22
 
712
  }
713
 
714
  #endregion
715
+ }
third-party/freemius/wordpress-sdk/require.php CHANGED
@@ -6,6 +6,10 @@
6
  * @since 1.1.9
7
  */
8
 
 
 
 
 
9
  // Configuration should be loaded first.
10
  require_once dirname( __FILE__ ) . '/config.php';
11
  require_once WP_FS__DIR_INCLUDES . '/fs-core-functions.php';
@@ -46,4 +50,4 @@
46
  require_once WP_FS__DIR_INCLUDES . '/class-fs-admin-notices.php';
47
  require_once WP_FS__DIR_INCLUDES . '/class-freemius-abstract.php';
48
  require_once WP_FS__DIR_INCLUDES . '/sdk/Exceptions/Exception.php';
49
- require_once WP_FS__DIR_INCLUDES . '/class-freemius.php';
6
  * @since 1.1.9
7
  */
8
 
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
  // Configuration should be loaded first.
14
  require_once dirname( __FILE__ ) . '/config.php';
15
  require_once WP_FS__DIR_INCLUDES . '/fs-core-functions.php';
50
  require_once WP_FS__DIR_INCLUDES . '/class-fs-admin-notices.php';
51
  require_once WP_FS__DIR_INCLUDES . '/class-freemius-abstract.php';
52
  require_once WP_FS__DIR_INCLUDES . '/sdk/Exceptions/Exception.php';
53
+ require_once WP_FS__DIR_INCLUDES . '/class-freemius.php';
third-party/freemius/wordpress-sdk/start.php CHANGED
@@ -15,7 +15,7 @@
15
  *
16
  * @var string
17
  */
18
- $this_sdk_version = '2.4.2';
19
 
20
  #region SDK Selection Logic --------------------------------------------------------------------
21
 
15
  *
16
  * @var string
17
  */
18
+ $this_sdk_version = '2.4.3';
19
 
20
  #region SDK Selection Logic --------------------------------------------------------------------
21
 
third-party/freemius/wordpress-sdk/templates/account/partials/addon.php CHANGED
@@ -1,4 +1,9 @@
1
  <?php
 
 
 
 
 
2
  /**
3
  * @var array $VARS
4
  * @var Freemius $fs
@@ -443,4 +448,4 @@
443
  </td>
444
  <!--/ Optional Delete Action -->
445
  <?php endif ?>
446
- </tr>
1
  <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
  /**
8
  * @var array $VARS
9
  * @var Freemius $fs
448
  </td>
449
  <!--/ Optional Delete Action -->
450
  <?php endif ?>
451
+ </tr>
third-party/freemius/wordpress-sdk/templates/ajax-loader.php CHANGED
@@ -1 +1,6 @@
1
- <div class="fs-ajax-loader" style="display: none"><?php for ( $i = 1; $i <= 8; $i ++ ) : ?><div class="fs-ajax-loader-bar fs-ajax-loader-bar-<?php echo $i ?>"></div><?php endfor ?></div>
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+ ?>
6
+ <div class="fs-ajax-loader" style="display: none"><?php for ( $i = 1; $i <= 8; $i ++ ) : ?><div class="fs-ajax-loader-bar fs-ajax-loader-bar-<?php echo $i ?>"></div><?php endfor ?></div>
third-party/freemius/wordpress-sdk/templates/debug.php CHANGED
@@ -37,6 +37,8 @@
37
 
38
  $.post( ajaxurl, {
39
  action: 'fs_toggle_debug_mode',
 
 
40
  is_on : ($(this).hasClass( 'fs-on' ) ? 1 : 0)
41
  }, function ( response ) {
42
  if ( 1 == response ) {
@@ -111,7 +113,8 @@
111
  if (optionName) {
112
  $.post(ajaxurl, {
113
  action : 'fs_get_db_option',
114
- _wpnonce : '<?php echo wp_create_nonce( 'fs_get_db_option' ) ?>',
 
115
  option_name: optionName
116
  }, function (response) {
117
  if (response.data.value)
@@ -131,7 +134,8 @@
131
  if (optionValue) {
132
  $.post(ajaxurl, {
133
  action : 'fs_set_db_option',
134
- _wpnonce : '<?php echo wp_create_nonce( 'fs_set_db_option' ) ?>',
 
135
  option_name : optionName,
136
  option_value: optionValue
137
  }, function () {
@@ -724,6 +728,8 @@
724
 
725
  $.post(ajaxurl, {
726
  action : 'fs_get_debug_log',
 
 
727
  filters: filters,
728
  offset : offset,
729
  limit : limit
37
 
38
  $.post( ajaxurl, {
39
  action: 'fs_toggle_debug_mode',
40
+ // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
41
+ _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_toggle_debug_mode' ) ); ?>,
42
  is_on : ($(this).hasClass( 'fs-on' ) ? 1 : 0)
43
  }, function ( response ) {
44
  if ( 1 == response ) {
113
  if (optionName) {
114
  $.post(ajaxurl, {
115
  action : 'fs_get_db_option',
116
+ // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
117
+ _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_db_option' ) ); ?>,
118
  option_name: optionName
119
  }, function (response) {
120
  if (response.data.value)
134
  if (optionValue) {
135
  $.post(ajaxurl, {
136
  action : 'fs_set_db_option',
137
+ // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
138
+ _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_set_db_option' ) ); ?>,
139
  option_name : optionName,
140
  option_value: optionValue
141
  }, function () {
728
 
729
  $.post(ajaxurl, {
730
  action : 'fs_get_debug_log',
731
+ // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
732
+ _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_get_debug_log' ) ); ?>,
733
  filters: filters,
734
  offset : offset,
735
  limit : limit
third-party/freemius/wordpress-sdk/templates/firewall-issues-js.php CHANGED
@@ -22,10 +22,12 @@
22
  notice = $( this ).parents( '.fs-notice' ),
23
  ajaxActionSuffix = notice.attr( 'data-manager-id' ).replace( ':', '-' );
24
 
25
- var data = {
26
- action : 'fs_resolve_firewall_issues_' + ajaxActionSuffix,
27
- error_type: error_type
28
- };
 
 
29
 
30
  if ( 'squid' === error_type ) {
31
  data.hosting_company = prompt( 'What is the name or URL of your hosting company?' );
@@ -39,7 +41,9 @@
39
  }
40
 
41
  if ( 'retry_ping' === error_type ) {
42
- data.action = 'fs_retry_connectivity_test_' + ajaxActionSuffix;
 
 
43
  }
44
 
45
  $( this ).css({'cursor': 'wait'});
@@ -56,4 +60,4 @@
56
  });
57
  });
58
  });
59
- </script>
22
  notice = $( this ).parents( '.fs-notice' ),
23
  ajaxActionSuffix = notice.attr( 'data-manager-id' ).replace( ':', '-' );
24
 
25
+ var data = {
26
+ action : 'fs_resolve_firewall_issues_' + ajaxActionSuffix,
27
+ // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
28
+ _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_resolve_firewall_issues' ) ); ?>,
29
+ error_type: error_type
30
+ };
31
 
32
  if ( 'squid' === error_type ) {
33
  data.hosting_company = prompt( 'What is the name or URL of your hosting company?' );
41
  }
42
 
43
  if ( 'retry_ping' === error_type ) {
44
+ data.action = 'fs_retry_connectivity_test_' + ajaxActionSuffix;
45
+ // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
46
+ data._wpnonce = <?php echo wp_json_encode( wp_create_nonce( 'fs_retry_connectivity_test' ) ); ?>;
47
  }
48
 
49
  $( this ).css({'cursor': 'wait'});
60
  });
61
  });
62
  });
63
+ </script>
third-party/freemius/wordpress-sdk/templates/partials/network-activation.php CHANGED
@@ -1,4 +1,9 @@
1
  <?php
 
 
 
 
 
2
  /**
3
  * @var array $VARS
4
  * @var Freemius $fs
@@ -86,4 +91,4 @@
86
  </tbody>
87
  </table>
88
  </div>
89
- </div>
1
  <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
  /**
8
  * @var array $VARS
9
  * @var Freemius $fs
91
  </tbody>
92
  </table>
93
  </div>
94
+ </div>
third-party/freemius/wordpress-sdk/templates/sticky-admin-notice-js.php CHANGED
@@ -23,7 +23,9 @@
23
 
24
  notice.fadeOut( 'fast', function() {
25
  var data = {
26
- action : 'fs_dismiss_notice_action_' + ajaxActionSuffix,
 
 
27
  message_id: id
28
  };
29
 
@@ -36,4 +38,4 @@
36
  });
37
  });
38
  });
39
- </script>
23
 
24
  notice.fadeOut( 'fast', function() {
25
  var data = {
26
+ action : 'fs_dismiss_notice_action_' + ajaxActionSuffix,
27
+ // As such we don't need to use `wp_json_encode` method but using it to follow wp.org guideline.
28
+ _wpnonce : <?php echo wp_json_encode( wp_create_nonce( 'fs_dismiss_notice_action' ) ); ?>,
29
  message_id: id
30
  };
31
 
38
  });
39
  });
40
  });
41
+ </script>
third-party/vendor/autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload.php @generated by Composer
4
+
5
+ require_once __DIR__ . '/composer/autoload_real.php';
6
+
7
+ return ComposerAutoloaderInit23aaeb6864f7a0ba3de7e8f407e351c7::getLoader();
third-party/vendor/composer/ClassLoader.php ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * This file is part of Composer.
5
+ *
6
+ * (c) Nils Adermann <naderman@naderman.de>
7
+ * Jordi Boggiano <j.boggiano@seld.be>
8
+ *
9
+ * For the full copyright and license information, please view the LICENSE
10
+ * file that was distributed with this source code.
11
+ */
12
+
13
+ namespace Composer\Autoload;
14
+
15
+ /**
16
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
17
+ *
18
+ * $loader = new \Composer\Autoload\ClassLoader();
19
+ *
20
+ * // register classes with namespaces
21
+ * $loader->add('Symfony\Component', __DIR__.'/component');
22
+ * $loader->add('Symfony', __DIR__.'/framework');
23
+ *
24
+ * // activate the autoloader
25
+ * $loader->register();
26
+ *
27
+ * // to enable searching the include path (eg. for PEAR packages)
28
+ * $loader->setUseIncludePath(true);
29
+ *
30
+ * In this example, if you try to use a class in the Symfony\Component
31
+ * namespace or one of its children (Symfony\Component\Console for instance),
32
+ * the autoloader will first look for the class under the component/
33
+ * directory, and it will then fallback to the framework/ directory if not
34
+ * found before giving up.
35
+ *
36
+ * This class is loosely based on the Symfony UniversalClassLoader.
37
+ *
38
+ * @author Fabien Potencier <fabien@symfony.com>
39
+ * @author Jordi Boggiano <j.boggiano@seld.be>
40
+ * @see http://www.php-fig.org/psr/psr-0/
41
+ * @see http://www.php-fig.org/psr/psr-4/
42
+ */
43
+ class ClassLoader
44
+ {
45
+ // PSR-4
46
+ private $prefixLengthsPsr4 = array();
47
+ private $prefixDirsPsr4 = array();
48
+ private $fallbackDirsPsr4 = array();
49
+
50
+ // PSR-0
51
+ private $prefixesPsr0 = array();
52
+ private $fallbackDirsPsr0 = array();
53
+
54
+ private $useIncludePath = false;
55
+ private $classMap = array();
56
+ private $classMapAuthoritative = false;
57
+ private $missingClasses = array();
58
+ private $apcuPrefix;
59
+
60
+ public function getPrefixes()
61
+ {
62
+ if (!empty($this->prefixesPsr0)) {
63
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
64
+ }
65
+
66
+ return array();
67
+ }
68
+
69
+ public function getPrefixesPsr4()
70
+ {
71
+ return $this->prefixDirsPsr4;
72
+ }
73
+
74
+ public function getFallbackDirs()
75
+ {
76
+ return $this->fallbackDirsPsr0;
77
+ }
78
+
79
+ public function getFallbackDirsPsr4()
80
+ {
81
+ return $this->fallbackDirsPsr4;
82
+ }
83
+
84
+ public function getClassMap()
85
+ {
86
+ return $this->classMap;
87
+ }
88
+
89
+ /**
90
+ * @param array $classMap Class to filename map
91
+ */
92
+ public function addClassMap(array $classMap)
93
+ {
94
+ if ($this->classMap) {
95
+ $this->classMap = array_merge($this->classMap, $classMap);
96
+ } else {
97
+ $this->classMap = $classMap;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Registers a set of PSR-0 directories for a given prefix, either
103
+ * appending or prepending to the ones previously set for this prefix.
104
+ *
105
+ * @param string $prefix The prefix
106
+ * @param array|string $paths The PSR-0 root directories
107
+ * @param bool $prepend Whether to prepend the directories
108
+ */
109
+ public function add($prefix, $paths, $prepend = false)
110
+ {
111
+ if (!$prefix) {
112
+ if ($prepend) {
113
+ $this->fallbackDirsPsr0 = array_merge(
114
+ (array) $paths,
115
+ $this->fallbackDirsPsr0
116
+ );
117
+ } else {
118
+ $this->fallbackDirsPsr0 = array_merge(
119
+ $this->fallbackDirsPsr0,
120
+ (array) $paths
121
+ );
122
+ }
123
+
124
+ return;
125
+ }
126
+
127
+ $first = $prefix[0];
128
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
129
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
130
+
131
+ return;
132
+ }
133
+ if ($prepend) {
134
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
135
+ (array) $paths,
136
+ $this->prefixesPsr0[$first][$prefix]
137
+ );
138
+ } else {
139
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
140
+ $this->prefixesPsr0[$first][$prefix],
141
+ (array) $paths
142
+ );
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Registers a set of PSR-4 directories for a given namespace, either
148
+ * appending or prepending to the ones previously set for this namespace.
149
+ *
150
+ * @param string $prefix The prefix/namespace, with trailing '\\'
151
+ * @param array|string $paths The PSR-4 base directories
152
+ * @param bool $prepend Whether to prepend the directories
153
+ *
154
+ * @throws \InvalidArgumentException
155
+ */
156
+ public function addPsr4($prefix, $paths, $prepend = false)
157
+ {
158
+ if (!$prefix) {
159
+ // Register directories for the root namespace.
160
+ if ($prepend) {
161
+ $this->fallbackDirsPsr4 = array_merge(
162
+ (array) $paths,
163
+ $this->fallbackDirsPsr4
164
+ );
165
+ } else {
166
+ $this->fallbackDirsPsr4 = array_merge(
167
+ $this->fallbackDirsPsr4,
168
+ (array) $paths
169
+ );
170
+ }
171
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
172
+ // Register directories for a new namespace.
173
+ $length = strlen($prefix);
174
+ if ('\\' !== $prefix[$length - 1]) {
175
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
176
+ }
177
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
178
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
179
+ } elseif ($prepend) {
180
+ // Prepend directories for an already registered namespace.
181
+ $this->prefixDirsPsr4[$prefix] = array_merge(
182
+ (array) $paths,
183
+ $this->prefixDirsPsr4[$prefix]
184
+ );
185
+ } else {
186
+ // Append directories for an already registered namespace.
187
+ $this->prefixDirsPsr4[$prefix] = array_merge(
188
+ $this->prefixDirsPsr4[$prefix],
189
+ (array) $paths
190
+ );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Registers a set of PSR-0 directories for a given prefix,
196
+ * replacing any others previously set for this prefix.
197
+ *
198
+ * @param string $prefix The prefix
199
+ * @param array|string $paths The PSR-0 base directories
200
+ */
201
+ public function set($prefix, $paths)
202
+ {
203
+ if (!$prefix) {
204
+ $this->fallbackDirsPsr0 = (array) $paths;
205
+ } else {
206
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Registers a set of PSR-4 directories for a given namespace,
212
+ * replacing any others previously set for this namespace.
213
+ *
214
+ * @param string $prefix The prefix/namespace, with trailing '\\'
215
+ * @param array|string $paths The PSR-4 base directories
216
+ *
217
+ * @throws \InvalidArgumentException
218
+ */
219
+ public function setPsr4($prefix, $paths)
220
+ {
221
+ if (!$prefix) {
222
+ $this->fallbackDirsPsr4 = (array) $paths;
223
+ } else {
224
+ $length = strlen($prefix);
225
+ if ('\\' !== $prefix[$length - 1]) {
226
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
227
+ }
228
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
229
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Turns on searching the include path for class files.
235
+ *
236
+ * @param bool $useIncludePath
237
+ */
238
+ public function setUseIncludePath($useIncludePath)
239
+ {
240
+ $this->useIncludePath = $useIncludePath;
241
+ }
242
+
243
+ /**
244
+ * Can be used to check if the autoloader uses the include path to check
245
+ * for classes.
246
+ *
247
+ * @return bool
248
+ */
249
+ public function getUseIncludePath()
250
+ {
251
+ return $this->useIncludePath;
252
+ }
253
+
254
+ /**
255
+ * Turns off searching the prefix and fallback directories for classes
256
+ * that have not been registered with the class map.
257
+ *
258
+ * @param bool $classMapAuthoritative
259
+ */
260
+ public function setClassMapAuthoritative($classMapAuthoritative)
261
+ {
262
+ $this->classMapAuthoritative = $classMapAuthoritative;
263
+ }
264
+
265
+ /**
266
+ * Should class lookup fail if not found in the current class map?
267
+ *
268
+ * @return bool
269
+ */
270
+ public function isClassMapAuthoritative()
271
+ {
272
+ return $this->classMapAuthoritative;
273
+ }
274
+
275
+ /**
276
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277
+ *
278
+ * @param string|null $apcuPrefix
279
+ */
280
+ public function setApcuPrefix($apcuPrefix)
281
+ {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
283
+ }
284
+
285
+ /**
286
+ * The APCu prefix in use, or null if APCu caching is not enabled.
287
+ *
288
+ * @return string|null
289
+ */
290
+ public function getApcuPrefix()
291
+ {
292
+ return $this->apcuPrefix;
293
+ }
294
+
295
+ /**
296
+ * Registers this instance as an autoloader.
297
+ *
298
+ * @param bool $prepend Whether to prepend the autoloader or not
299
+ */
300
+ public function register($prepend = false)
301
+ {
302
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
303
+ }
304
+
305
+ /**
306
+ * Unregisters this instance as an autoloader.
307
+ */
308
+ public function unregister()
309
+ {
310
+ spl_autoload_unregister(array($this, 'loadClass'));
311
+ }
312
+
313
+ /**
314
+ * Loads the given class or interface.
315
+ *
316
+ * @param string $class The name of the class
317
+ * @return bool|null True if loaded, null otherwise
318
+ */
319
+ public function loadClass($class)
320
+ {
321
+ if ($file = $this->findFile($class)) {
322
+ includeFile($file);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Finds the path to the file where the class is defined.
330
+ *
331
+ * @param string $class The name of the class
332
+ *
333
+ * @return string|false The path if found, false otherwise
334
+ */
335
+ public function findFile($class)
336
+ {
337
+ // class map lookup
338
+ if (isset($this->classMap[$class])) {
339
+ return $this->classMap[$class];
340
+ }
341
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
342
+ return false;
343
+ }
344
+ if (null !== $this->apcuPrefix) {
345
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
346
+ if ($hit) {
347
+ return $file;
348
+ }
349
+ }
350
+
351
+ $file = $this->findFileWithExtension($class, '.php');
352
+
353
+ // Search for Hack files if we are running on HHVM
354
+ if (false === $file && defined('HHVM_VERSION')) {
355
+ $file = $this->findFileWithExtension($class, '.hh');
356
+ }
357
+
358
+ if (null !== $this->apcuPrefix) {
359
+ apcu_add($this->apcuPrefix.$class, $file);
360
+ }
361
+
362
+ if (false === $file) {
363
+ // Remember that this class does not exist.
364
+ $this->missingClasses[$class] = true;
365
+ }
366
+
367
+ return $file;
368
+ }
369
+
370
+ private function findFileWithExtension($class, $ext)
371
+ {
372
+ // PSR-4 lookup
373
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
374
+
375
+ $first = $class[0];
376
+ if (isset($this->prefixLengthsPsr4[$first])) {
377
+ $subPath = $class;
378
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
379
+ $subPath = substr($subPath, 0, $lastPos);
380
+ $search = $subPath . '\\';
381
+ if (isset($this->prefixDirsPsr4[$search])) {
382
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
383
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
384
+ if (file_exists($file = $dir . $pathEnd)) {
385
+ return $file;
386
+ }
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ // PSR-4 fallback dirs
393
+ foreach ($this->fallbackDirsPsr4 as $dir) {
394
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
395
+ return $file;
396
+ }
397
+ }
398
+
399
+ // PSR-0 lookup
400
+ if (false !== $pos = strrpos($class, '\\')) {
401
+ // namespaced class name
402
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
403
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
404
+ } else {
405
+ // PEAR-like class name
406
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
407
+ }
408
+
409
+ if (isset($this->prefixesPsr0[$first])) {
410
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
411
+ if (0 === strpos($class, $prefix)) {
412
+ foreach ($dirs as $dir) {
413
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
414
+ return $file;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // PSR-0 fallback dirs
422
+ foreach ($this->fallbackDirsPsr0 as $dir) {
423
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
424
+ return $file;
425
+ }
426
+ }
427
+
428
+ // PSR-0 include paths.
429
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
430
+ return $file;
431
+ }
432
+
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Scope isolated include.
439
+ *
440
+ * Prevents access to $this/self from included files.
441
+ */
442
+ function includeFile($file)
443
+ {
444
+ include $file;
445
+ }
third-party/vendor/composer/LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Copyright (c) Nils Adermann, Jordi Boggiano
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is furnished
9
+ to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
21
+
third-party/vendor/composer/autoload_classmap.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_classmap.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ 'WSAL_Vendor\\MirazMac\\Requirements\\Checker' => $vendorDir . '/mirazmac/php-requirements-checker/src/Checker.php',
10
+ 'WSAL_Vendor\\WP_Async_Request' => $vendorDir . '/deliciousbrains/wp-background-processing/classes/wp-async-request.php',
11
+ 'WSAL_Vendor\\WP_Background_Process' => $vendorDir . '/deliciousbrains/wp-background-processing/classes/wp-background-process.php',
12
+ );
third-party/vendor/composer/autoload_namespaces.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_namespaces.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
third-party/vendor/composer/autoload_psr4.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_psr4.php @generated by Composer
4
+
5
+ $vendorDir = dirname(dirname(__FILE__));
6
+ $baseDir = dirname($vendorDir);
7
+
8
+ return array(
9
+ );
third-party/vendor/composer/autoload_real.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_real.php @generated by Composer
4
+
5
+ class ComposerAutoloaderInit23aaeb6864f7a0ba3de7e8f407e351c7
6
+ {
7
+ private static $loader;
8
+
9
+ public static function loadClassLoader($class)
10
+ {
11
+ if ('Composer\Autoload\ClassLoader' === $class) {
12
+ require __DIR__ . '/ClassLoader.php';
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @return \Composer\Autoload\ClassLoader
18
+ */
19
+ public static function getLoader()
20
+ {
21
+ if (null !== self::$loader) {
22
+ return self::$loader;
23
+ }
24
+
25
+ spl_autoload_register(array('ComposerAutoloaderInit23aaeb6864f7a0ba3de7e8f407e351c7', 'loadClassLoader'), true, true);
26
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27
+ spl_autoload_unregister(array('ComposerAutoloaderInit23aaeb6864f7a0ba3de7e8f407e351c7', 'loadClassLoader'));
28
+
29
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
+ if ($useStaticLoader) {
31
+ require_once __DIR__ . '/autoload_static.php';
32
+
33
+ call_user_func(\Composer\Autoload\ComposerStaticInit23aaeb6864f7a0ba3de7e8f407e351c7::getInitializer($loader));
34
+ } else {
35
+ $classMap = require __DIR__ . '/autoload_classmap.php';
36
+ if ($classMap) {
37
+ $loader->addClassMap($classMap);
38
+ }
39
+ }
40
+
41
+ $loader->setClassMapAuthoritative(true);
42
+ $loader->register(true);
43
+
44
+ return $loader;
45
+ }
46
+ }
third-party/vendor/composer/autoload_static.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // autoload_static.php @generated by Composer
4
+
5
+ namespace Composer\Autoload;
6
+
7
+ class ComposerStaticInit23aaeb6864f7a0ba3de7e8f407e351c7
8
+ {
9
+ public static $classMap = array (
10
+ 'WSAL_Vendor\\MirazMac\\Requirements\\Checker' => __DIR__ . '/..' . '/mirazmac/php-requirements-checker/src/Checker.php',
11
+ 'WSAL_Vendor\\WP_Async_Request' => __DIR__ . '/..' . '/deliciousbrains/wp-background-processing/classes/wp-async-request.php',
12
+ 'WSAL_Vendor\\WP_Background_Process' => __DIR__ . '/..' . '/deliciousbrains/wp-background-processing/classes/wp-background-process.php',
13
+ );
14
+
15
+ public static function getInitializer(ClassLoader $loader)
16
+ {
17
+ return \Closure::bind(function () use ($loader) {
18
+ $loader->classMap = ComposerStaticInit23aaeb6864f7a0ba3de7e8f407e351c7::$classMap;
19
+ }, null, ClassLoader::class);
20
+ }
21
+ }
third-party/vendor/deliciousbrains/wp-background-processing/classes/wp-async-request.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WSAL_Vendor;
4
+
5
+ /**
6
+ * WP Async Request
7
+ *
8
+ * @package WP-Background-Processing
9
+ */
10
+ /**
11
+ * Abstract WP_Async_Request class.
12
+ *
13
+ * @abstract
14
+ */
15
+ abstract class WP_Async_Request
16
+ {
17
+ /**
18
+ * Prefix
19
+ *
20
+ * (default value: 'wp')
21
+ *
22
+ * @var string
23
+ * @access protected
24
+ */
25
+ protected $prefix = 'wp';
26
+ /**
27
+ * Action
28
+ *
29
+ * (default value: 'async_request')
30
+ *
31
+ * @var string
32
+ * @access protected
33
+ */
34
+ protected $action = 'async_request';
35
+ /**
36
+ * Identifier
37
+ *
38
+ * @var mixed
39
+ * @access protected
40
+ */
41
+ protected $identifier;
42
+ /**
43
+ * Data
44
+ *
45
+ * (default value: array())
46
+ *
47
+ * @var array
48
+ * @access protected
49
+ */
50
+ protected $data = array();
51
+ /**
52
+ * Initiate new async request
53
+ */
54
+ public function __construct()
55
+ {
56
+ $this->identifier = $this->prefix . '_' . $this->action;
57
+ add_action('wp_ajax_' . $this->identifier, array($this, 'maybe_handle'));
58
+ add_action('wp_ajax_nopriv_' . $this->identifier, array($this, 'maybe_handle'));
59
+ }
60
+ /**
61
+ * Set data used during the request
62
+ *
63
+ * @param array $data Data.
64
+ *
65
+ * @return $this
66
+ */
67
+ public function data($data)
68
+ {
69
+ $this->data = $data;
70
+ return $this;
71
+ }
72
+ /**
73
+ * Dispatch the async request
74
+ *
75
+ * @return array|WP_Error
76
+ */
77
+ public function dispatch()
78
+ {
79
+ $url = add_query_arg($this->get_query_args(), $this->get_query_url());
80
+ $args = $this->get_post_args();
81
+ return wp_remote_post(esc_url_raw($url), $args);
82
+ }
83
+ /**
84
+ * Get query args
85
+ *
86
+ * @return array
87
+ */
88
+ protected function get_query_args()
89
+ {
90
+ if (\property_exists($this, 'query_args')) {
91
+ return $this->query_args;
92
+ }
93
+ $args = array('action' => $this->identifier, 'nonce' => wp_create_nonce($this->identifier));
94
+ /**
95
+ * Filters the post arguments used during an async request.
96
+ *
97
+ * @param array $url
98
+ */
99
+ return apply_filters($this->identifier . '_query_args', $args);
100
+ }
101
+ /**
102
+ * Get query URL
103
+ *
104
+ * @return string
105
+ */
106
+ protected function get_query_url()
107
+ {
108
+ if (\property_exists($this, 'query_url')) {
109
+ return $this->query_url;
110
+ }
111
+ $url = admin_url('admin-ajax.php');
112
+ /**
113
+ * Filters the post arguments used during an async request.
114
+ *
115
+ * @param string $url
116
+ */
117
+ return apply_filters($this->identifier . '_query_url', $url);
118
+ }
119
+ /**
120
+ * Get post args
121
+ *
122
+ * @return array
123
+ */
124
+ protected function get_post_args()
125
+ {
126
+ if (\property_exists($this, 'post_args')) {
127
+ return $this->post_args;
128
+ }
129
+ $args = array('timeout' => 0.01, 'blocking' => \false, 'body' => $this->data, 'cookies' => $_COOKIE, 'sslverify' => apply_filters('https_local_ssl_verify', \false));
130
+ /**
131
+ * Filters the post arguments used during an async request.
132
+ *
133
+ * @param array $args
134
+ */
135
+ return apply_filters($this->identifier . '_post_args', $args);
136
+ }
137
+ /**
138
+ * Maybe handle
139
+ *
140
+ * Check for correct nonce and pass to handler.
141
+ */
142
+ public function maybe_handle()
143
+ {
144
+ // Don't lock up other requests while processing
145
+ \session_write_close();
146
+ check_ajax_referer($this->identifier, 'nonce');
147
+ $this->handle();
148
+ wp_die();
149
+ }
150
+ /**
151
+ * Handle
152
+ *
153
+ * Override this method to perform any actions required
154
+ * during the async request.
155
+ */
156
+ protected abstract function handle();
157
+ }
third-party/vendor/deliciousbrains/wp-background-processing/classes/wp-background-process.php ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WSAL_Vendor;
4
+
5
+ /**
6
+ * WP Background Process
7
+ *
8
+ * @package WP-Background-Processing
9
+ */
10
+ /**
11
+ * Abstract WP_Background_Process class.
12
+ *
13
+ * @abstract
14
+ * @extends WP_Async_Request
15
+ */
16
+ abstract class WP_Background_Process extends WP_Async_Request
17
+ {
18
+ /**
19
+ * Action
20
+ *
21
+ * (default value: 'background_process')
22
+ *
23
+ * @var string
24
+ * @access protected
25
+ */
26
+ protected $action = 'background_process';
27
+ /**
28
+ * Start time of current process.
29
+ *
30
+ * (default value: 0)
31
+ *
32
+ * @var int
33
+ * @access protected
34
+ */
35
+ protected $start_time = 0;
36
+ /**
37
+ * Cron_hook_identifier
38
+ *
39
+ * @var mixed
40
+ * @access protected
41
+ */
42
+ protected $cron_hook_identifier;
43
+ /**
44
+ * Cron_interval_identifier
45
+ *
46
+ * @var mixed
47
+ * @access protected
48
+ */
49
+ protected $cron_interval_identifier;
50
+ /**
51
+ * Initiate new background process
52
+ */
53
+ public function __construct()
54
+ {
55
+ parent::__construct();
56
+ $this->cron_hook_identifier = $this->identifier . '_cron';
57
+ $this->cron_interval_identifier = $this->identifier . '_cron_interval';
58
+ add_action($this->cron_hook_identifier, array($this, 'handle_cron_healthcheck'));
59
+ add_filter('cron_schedules', array($this, 'schedule_cron_healthcheck'));
60
+ }
61
+ /**
62
+ * Dispatch
63
+ *
64
+ * @access public
65
+ * @return void
66
+ */
67
+ public function dispatch()
68
+ {
69
+ // Schedule the cron healthcheck.
70
+ $this->schedule_event();
71
+ // Perform remote post.
72
+ return parent::dispatch();
73
+ }
74
+ /**
75
+ * Push to queue
76
+ *
77
+ * @param mixed $data Data.
78
+ *
79
+ * @return $this
80
+ */
81
+ public function push_to_queue($data)
82
+ {
83
+ $this->data[] = $data;
84
+ return $this;
85
+ }
86
+ /**
87
+ * Save queue
88
+ *
89
+ * @return $this
90
+ */
91
+ public function save()
92
+ {
93
+ $key = $this->generate_key();
94
+ if (!empty($this->data)) {
95
+ update_site_option($key, $this->data);
96
+ }
97
+ return $this;
98
+ }
99
+ /**
100
+ * Update queue
101
+ *
102
+ * @param string $key Key.
103
+ * @param array $data Data.
104
+ *
105
+ * @return $this
106
+ */
107
+ public function update($key, $data)
108
+ {
109
+ if (!empty($data)) {
110
+ update_site_option($key, $data);
111
+ }
112
+ return $this;
113
+ }
114
+ /**
115
+ * Delete queue
116
+ *
117
+ * @param string $key Key.
118
+ *
119
+ * @return $this
120
+ */
121
+ public function delete($key)
122
+ {
123
+ delete_site_option($key);
124
+ return $this;
125
+ }
126
+ /**
127
+ * Generate key
128
+ *
129
+ * Generates a unique key based on microtime. Queue items are
130
+ * given a unique key so that they can be merged upon save.
131
+ *
132
+ * @param int $length Length.
133
+ *
134
+ * @return string
135
+ */
136
+ protected function generate_key($length = 64)
137
+ {
138
+ $unique = \md5(\microtime() . \rand());
139
+ $prepend = $this->identifier . '_batch_';
140
+ return \substr($prepend . $unique, 0, $length);
141
+ }
142
+ /**
143
+ * Maybe process queue
144
+ *
145
+ * Checks whether data exists within the queue and that
146
+ * the process is not already running.
147
+ */
148
+ public function maybe_handle()
149
+ {
150
+ // Don't lock up other requests while processing
151
+ \session_write_close();
152
+ if ($this->is_process_running()) {
153
+ // Background process already running.
154
+ wp_die();
155
+ }
156
+ if ($this->is_queue_empty()) {
157
+ // No data to process.
158
+ wp_die();
159
+ }
160
+ check_ajax_referer($this->identifier, 'nonce');
161
+ $this->handle();
162
+ wp_die();
163
+ }
164
+ /**
165
+ * Is queue empty
166
+ *
167
+ * @return bool
168
+ */
169
+ protected function is_queue_empty()
170
+ {
171
+ global $wpdb;
172
+ $table = $wpdb->options;
173
+ $column = 'option_name';
174
+ if (is_multisite()) {
175
+ $table = $wpdb->sitemeta;
176
+ $column = 'meta_key';
177
+ }
178
+ $key = $wpdb->esc_like($this->identifier . '_batch_') . '%';
179
+ $count = $wpdb->get_var($wpdb->prepare("\n\t\t\tSELECT COUNT(*)\n\t\t\tFROM {$table}\n\t\t\tWHERE {$column} LIKE %s\n\t\t", $key));
180
+ return $count > 0 ? \false : \true;
181
+ }
182
+ /**
183
+ * Is process running
184
+ *
185
+ * Check whether the current process is already running
186
+ * in a background process.
187
+ */
188
+ protected function is_process_running()
189
+ {
190
+ if (get_site_transient($this->identifier . '_process_lock')) {
191
+ // Process already running.
192
+ return \true;
193
+ }
194
+ return \false;
195
+ }
196
+ /**
197
+ * Lock process
198
+ *
199
+ * Lock the process so that multiple instances can't run simultaneously.
200
+ * Override if applicable, but the duration should be greater than that
201
+ * defined in the time_exceeded() method.
202
+ */
203
+ protected function lock_process()
204
+ {
205
+ $this->start_time = \time();
206
+ // Set start time of current process.
207
+ $lock_duration = \property_exists($this, 'queue_lock_time') ? $this->queue_lock_time : 60;
208
+ // 1 minute
209
+ $lock_duration = apply_filters($this->identifier . '_queue_lock_time', $lock_duration);
210
+ set_site_transient($this->identifier . '_process_lock', \microtime(), $lock_duration);
211
+ }
212
+ /**
213
+ * Unlock process
214
+ *
215
+ * Unlock the process so that other instances can spawn.
216
+ *
217
+ * @return $this
218
+ */
219
+ protected function unlock_process()
220
+ {
221
+ delete_site_transient($this->identifier . '_process_lock');
222
+ return $this;
223
+ }
224
+ /**
225
+ * Get batch
226
+ *
227
+ * @return stdClass Return the first batch from the queue
228
+ */
229
+ protected function get_batch()
230
+ {
231
+ global $wpdb;
232
+ $table = $wpdb->options;
233
+ $column = 'option_name';
234
+ $key_column = 'option_id';
235
+ $value_column = 'option_value';
236
+ if (is_multisite()) {
237
+ $table = $wpdb->sitemeta;
238
+ $column = 'meta_key';
239
+ $key_column = 'meta_id';
240
+ $value_column = 'meta_value';
241
+ }
242
+ $key = $wpdb->esc_like($this->identifier . '_batch_') . '%';
243
+ $query = $wpdb->get_row($wpdb->prepare("\n\t\t\tSELECT *\n\t\t\tFROM {$table}\n\t\t\tWHERE {$column} LIKE %s\n\t\t\tORDER BY {$key_column} ASC\n\t\t\tLIMIT 1\n\t\t", $key));
244
+ $batch = new \stdClass();
245
+ $batch->key = $query->{$column};
246
+ $batch->data = maybe_unserialize($query->{$value_column});
247
+ return $batch;
248
+ }
249
+ /**
250
+ * Handle
251
+ *
252
+ * Pass each queue item to the task handler, while remaining
253
+ * within server memory and time limit constraints.
254
+ */
255
+ protected function handle()
256
+ {
257
+ $this->lock_process();
258
+ do {
259
+ $batch = $this->get_batch();
260
+ foreach ($batch->data as $key => $value) {
261
+ $task = $this->task($value);
262
+ if (\false !== $task) {
263
+ $batch->data[$key] = $task;
264
+ } else {
265
+ unset($batch->data[$key]);
266
+ }
267
+ if ($this->time_exceeded() || $this->memory_exceeded()) {
268
+ // Batch limits reached.
269
+ break;
270
+ }
271
+ }
272
+ // Update or delete current batch.
273
+ if (!empty($batch->data)) {
274
+ $this->update($batch->key, $batch->data);
275
+ } else {
276
+ $this->delete($batch->key);
277
+ }
278
+ } while (!$this->time_exceeded() && !$this->memory_exceeded() && !$this->is_queue_empty());
279
+ $this->unlock_process();
280
+ // Start next batch or complete process.
281
+ if (!$this->is_queue_empty()) {
282
+ $this->dispatch();
283
+ } else {
284
+ $this->complete();
285
+ }
286
+ wp_die();
287
+ }
288
+ /**
289
+ * Memory exceeded
290
+ *
291
+ * Ensures the batch process never exceeds 90%
292
+ * of the maximum WordPress memory.
293
+ *
294
+ * @return bool
295
+ */
296
+ protected function memory_exceeded()
297
+ {
298
+ $memory_limit = $this->get_memory_limit() * 0.9;
299
+ // 90% of max memory
300
+ $current_memory = \memory_get_usage(\true);
301
+ $return = \false;
302
+ if ($current_memory >= $memory_limit) {
303
+ $return = \true;
304
+ }
305
+ return apply_filters($this->identifier . '_memory_exceeded', $return);
306
+ }
307
+ /**
308
+ * Get memory limit
309
+ *
310
+ * @return int
311
+ */
312
+ protected function get_memory_limit()
313
+ {
314
+ if (\function_exists('ini_get')) {
315
+ $memory_limit = \ini_get('memory_limit');
316
+ } else {
317
+ // Sensible default.
318
+ $memory_limit = '128M';
319
+ }
320
+ if (!$memory_limit || -1 === \intval($memory_limit)) {
321
+ // Unlimited, set to 32GB.
322
+ $memory_limit = '32000M';
323
+ }
324
+ return wp_convert_hr_to_bytes($memory_limit);
325
+ }
326
+ /**
327
+ * Time exceeded.
328
+ *
329
+ * Ensures the batch never exceeds a sensible time limit.
330
+ * A timeout limit of 30s is common on shared hosting.
331
+ *
332
+ * @return bool
333
+ */
334
+ protected function time_exceeded()
335
+ {
336
+ $finish = $this->start_time + apply_filters($this->identifier . '_default_time_limit', 20);
337
+ // 20 seconds
338
+ $return = \false;
339
+ if (\time() >= $finish) {
340
+ $return = \true;
341
+ }
342
+ return apply_filters($this->identifier . '_time_exceeded', $return);
343
+ }
344
+ /**
345
+ * Complete.
346
+ *
347
+ * Override if applicable, but ensure that the below actions are
348
+ * performed, or, call parent::complete().
349
+ */
350
+ protected function complete()
351
+ {
352
+ // Unschedule the cron healthcheck.
353
+ $this->clear_scheduled_event();
354
+ }
355
+ /**
356
+ * Schedule cron healthcheck
357
+ *
358
+ * @access public
359
+ *
360
+ * @param mixed $schedules Schedules.
361
+ *
362
+ * @return mixed
363
+ */
364
+ public function schedule_cron_healthcheck($schedules)
365
+ {
366
+ $interval = apply_filters($this->identifier . '_cron_interval', 5);
367
+ if (\property_exists($this, 'cron_interval')) {
368
+ $interval = apply_filters($this->identifier . '_cron_interval', $this->cron_interval);
369
+ }
370
+ // Adds every 5 minutes to the existing schedules.
371
+ $schedules[$this->identifier . '_cron_interval'] = array('interval' => \MINUTE_IN_SECONDS * $interval, 'display' => \sprintf(__('Every %d Minutes'), $interval));
372
+ return $schedules;
373
+ }
374
+ /**
375
+ * Handle cron healthcheck
376
+ *
377
+ * Restart the background process if not already running
378
+ * and data exists in the queue.
379
+ */
380
+ public function handle_cron_healthcheck()
381
+ {
382
+ if ($this->is_process_running()) {
383
+ // Background process already running.
384
+ exit;
385
+ }
386
+ if ($this->is_queue_empty()) {
387
+ // No data to process.
388
+ $this->clear_scheduled_event();
389
+ exit;
390
+ }
391
+ $this->handle();
392
+ exit;
393
+ }
394
+ /**
395
+ * Schedule event
396
+ */
397
+ protected function schedule_event()
398
+ {
399
+ if (!wp_next_scheduled($this->cron_hook_identifier)) {
400
+ wp_schedule_event(\time(), $this->cron_interval_identifier, $this->cron_hook_identifier);
401
+ }
402
+ }
403
+ /**
404
+ * Clear scheduled event
405
+ */
406
+ protected function clear_scheduled_event()
407
+ {
408
+ $timestamp = wp_next_scheduled($this->cron_hook_identifier);
409
+ if ($timestamp) {
410
+ wp_unschedule_event($timestamp, $this->cron_hook_identifier);
411
+ }
412
+ }
413
+ /**
414
+ * Cancel Process
415
+ *
416
+ * Stop processing queue items, clear cronjob and delete batch.
417
+ *
418
+ */
419
+ public function cancel_process()
420
+ {
421
+ if (!$this->is_queue_empty()) {
422
+ $batch = $this->get_batch();
423
+ $this->delete($batch->key);
424
+ wp_clear_scheduled_hook($this->cron_hook_identifier);
425
+ }
426
+ }
427
+ /**
428
+ * Task
429
+ *
430
+ * Override this method to perform any actions required on each
431
+ * queue item. Return the modified item for further processing
432
+ * in the next pass through. Or, return false to remove the
433
+ * item from the queue.
434
+ *
435
+ * @param mixed $item Queue item to iterate over.
436
+ *
437
+ * @return mixed
438
+ */
439
+ protected abstract function task($item);
440
+ }
third-party/vendor/deliciousbrains/wp-background-processing/wp-background-processing.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WSAL_Vendor;
4
+
5
+ /**
6
+ * WP-Background Processing
7
+ *
8
+ * @package WP-Background-Processing
9
+ */
10
+ /*
11
+ Plugin Name: WP Background Processing
12
+ Plugin URI: https://github.com/A5hleyRich/wp-background-processing
13
+ Description: Asynchronous requests and background processing in WordPress.
14
+ Author: Delicious Brains Inc.
15
+ Version: 1.0
16
+ Author URI: https://deliciousbrains.com/
17
+ GitHub Plugin URI: https://github.com/A5hleyRich/wp-background-processing
18
+ GitHub Branch: master
19
+ */
20
+ if (!\class_exists('WSAL_Vendor\\WP_Async_Request')) {
21
+ require_once plugin_dir_path(__FILE__) . 'classes/wp-async-request.php';
22
+ }
23
+ if (!\class_exists('WSAL_Vendor\\WP_Background_Process')) {
24
+ require_once plugin_dir_path(__FILE__) . 'classes/wp-background-process.php';
25
+ }
third-party/vendor/mirazmac/php-requirements-checker/src/Checker.php ADDED
@@ -0,0 +1,780 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WSAL_Vendor\MirazMac\Requirements;
4
+
5
+ /**
6
+ * PHP Requirements Checker
7
+ *
8
+ * A quick library to check the current environment against a set of defined requirements.
9
+ * Currently it supports checking for PHP version, OS, Extensions, PHP INI values,
10
+ * Functions, Classes, Apache Modules and local Files and Folders.
11
+ *
12
+ * @author Miraz Mac <mirazmac@gmail.com>
13
+ * @link https://mirazmac.com Author Homepage
14
+ * @version 0.1
15
+ * @license LICENSE The MIT License
16
+ * @package MirazMac\Requirements
17
+ */
18
+ class Checker
19
+ {
20
+ /**
21
+ * Identifier for is_file() check
22
+ */
23
+ const CHECK_IS_FILE = 'is_file';
24
+ /**
25
+ * Identifier for is_dir() check
26
+ */
27
+ const CHECK_IS_DIR = 'is_dir';
28
+ /**
29
+ * Identifier for is_readable() check
30
+ */
31
+ const CHECK_IS_READABLE = 'is_readable';
32
+ /**
33
+ * Identifier for is_writable() check
34
+ */
35
+ const CHECK_IS_WRITABLE = 'is_writable';
36
+ /**
37
+ * Identifier for file_exists() check
38
+ */
39
+ const CHECK_FILE_EXISTS = 'file_exists';
40
+ /**
41
+ * Identifier for Unix
42
+ */
43
+ const OS_UNIX = 'UNIX';
44
+ /**
45
+ * Identifier for Windows
46
+ */
47
+ const OS_DOS = 'DOS';
48
+ /**
49
+ * Requirements as an array
50
+ *
51
+ * @var array
52
+ */
53
+ protected $requirements;
54
+ /**
55
+ * Parsed state of the requirements
56
+ *
57
+ * @var array
58
+ */
59
+ protected $parsedRequirements;
60
+ /**
61
+ * Stores if current requirements criteria is satisfied or not
62
+ *
63
+ * @var boolean
64
+ */
65
+ protected $satisfied = \true;
66
+ /**
67
+ * Basic locales for comparison operators
68
+ *
69
+ * @var array
70
+ */
71
+ protected $locale = ['>' => 'greater than', '=' => 'equal to', '<' => 'lower than', '>=' => 'greater than or equal to', '=<' => 'lower than or equal to'];
72
+ /**
73
+ * Array of the total errors
74
+ *
75
+ * @var array
76
+ */
77
+ protected $errors = [];
78
+ /**
79
+ * Constructor
80
+ */
81
+ public function __construct()
82
+ {
83
+ $this->resetRequirements();
84
+ }
85
+ /**
86
+ * Perform checks on the passed requirements
87
+ *
88
+ * @return array The parsed requirements
89
+ */
90
+ public function check()
91
+ {
92
+ $this->parsedRequirements['system'] = $this->validateSystemRequirement();
93
+ $this->parsedRequirements['extensions'] = $this->validateExtensionRequirement();
94
+ $this->parsedRequirements['apache_modules'] = $this->validateApacheModuleRequirement();
95
+ $this->parsedRequirements['functions'] = $this->validateFunctionRequirement();
96
+ $this->parsedRequirements['classes'] = $this->validateClassRequirement();
97
+ $this->parsedRequirements['ini_values'] = $this->validateIniRequirement();
98
+ $this->parsedRequirements['files'] = $this->validateFileRequirement();
99
+ return $this->parsedRequirements;
100
+ }
101
+ /**
102
+ * Resets the requirements to default
103
+ *
104
+ * @return Checker
105
+ */
106
+ public function resetRequirements()
107
+ {
108
+ $default = ['system' => ['php_version' => null, 'os' => null], 'ini_values' => [], 'files' => [], 'extensions' => [], 'classes' => [], 'functions' => [], 'apache_modules' => []];
109
+ $this->requirements = $default;
110
+ $this->parsedRequirements = $default;
111
+ return $this;
112
+ }
113
+ /**
114
+ * Return the requirements added currently
115
+ *
116
+ * @return array
117
+ */
118
+ public function getRequirements()
119
+ {
120
+ return $this->requirements;
121
+ }
122
+ /**
123
+ * Returns if the last requirement check was satisfying or not
124
+ *
125
+ * @return boolean
126
+ */
127
+ public function isSatisfied()
128
+ {
129
+ return $this->satisfied;
130
+ }
131
+ /**
132
+ * Returns all the errors as array
133
+ *
134
+ * @return array
135
+ */
136
+ public function getErrors()
137
+ {
138
+ return $this->errors;
139
+ }
140
+ /**
141
+ * Validates only the PHP INI requirements
142
+ *
143
+ * @return array
144
+ */
145
+ public function validateIniRequirement()
146
+ {
147
+ $values = [];
148
+ foreach ($this->requirements['ini_values'] as $key => $value) {
149
+ $setting = \ini_get($key);
150
+ if ($setting == 'On' || $setting == '1') {
151
+ $setting = \true;
152
+ } elseif ($setting == 'Off' || $setting == '' || $setting == '0') {
153
+ $setting = \false;
154
+ }
155
+ $data = $this->getParsedStructure();
156
+ $data['preferred'] = $value;
157
+ $data['current'] = $setting;
158
+ // So you only want to show the value?
159
+ // No validation? Fine.
160
+ if (\is_null($value)) {
161
+ $data['satisfied'] = \true;
162
+ $values[$key] = $data;
163
+ continue;
164
+ }
165
+ if (\is_bool($value)) {
166
+ $data['preferred'] = $value ? 'On' : 'Off';
167
+ $data['current'] = $setting ? 'On' : 'Off';
168
+ if ($value) {
169
+ $data['satisfied'] = $setting;
170
+ } else {
171
+ $data['satisfied'] = !$setting;
172
+ }
173
+ if (!$data['satisfied']) {
174
+ $this->satisfied = \false;
175
+ $data['message'] = "The php.ini setting `{$key}` should be {$data['preferred']}, currently it's set to {$data['current']}";
176
+ $this->errors[] = $data['message'];
177
+ }
178
+ $values[$key] = $data;
179
+ continue;
180
+ }
181
+ $parsed = $this->parseComparisonString($value, '=');
182
+ $value = $parsed['plain'];
183
+ $operator = $parsed['operator'];
184
+ $newcfg = $this->returnBytes($setting);
185
+ $newval = $this->returnBytes($value);
186
+ // Acknowledge '-1'(unlimited) values
187
+ // @see https://github.com/MirazMac/php-requirements-checker/issues/2
188
+ $data['satisfied'] = $setting == '-1' ? \true : $this->looseComparison($newcfg, $operator, $newval);
189
+ if (!$data['satisfied']) {
190
+ $this->satisfied = \false;
191
+ $data['message'] = "The php.ini setting `{$key}` should be {$this->locale[$operator]} {$value}. Currently it's set to {$setting}";
192
+ $this->errors[] = $data['message'];
193
+ }
194
+ $values[$key] = $data;
195
+ }
196
+ return $values;
197
+ }
198
+ /**
199
+ * Validates only the classes requirement
200
+ *
201
+ * @return array
202
+ */
203
+ public function validateClassRequirement()
204
+ {
205
+ $values = [];
206
+ foreach ($this->requirements['classes'] as $key => $class) {
207
+ $data = $this->getParsedStructure();
208
+ $data['preferred'] = $class;
209
+ $data['current'] = \false;
210
+ $satisfied = \false;
211
+ foreach (\explode('|', $class) as $className) {
212
+ if (\class_exists($this->ensureNamespace($className))) {
213
+ $satisfied = \true;
214
+ break;
215
+ }
216
+ }
217
+ if ($satisfied) {
218
+ $data['satisfied'] = \true;
219
+ $data['current'] = \true;
220
+ } else {
221
+ $this->satisfied = \false;
222
+ $data['message'] = "Class `{$class}` is not defined";
223
+ $this->errors[] = $data['message'];
224
+ }
225
+ $values[$class] = $data;
226
+ }
227
+ return $values;
228
+ }
229
+ /**
230
+ * Validates apache module requirements
231
+ *
232
+ * @return array
233
+ */
234
+ public function validateApacheModuleRequirement()
235
+ {
236
+ $values = [];
237
+ // Run Only in apache servers
238
+ if (!\function_exists('apache_get_modules')) {
239
+ return $values;
240
+ }
241
+ $modules = \apache_get_modules();
242
+ foreach ($this->requirements['apache_modules'] as $key => $module) {
243
+ $structure = $this->getParsedStructure();
244
+ $structure['preferred'] = $module;
245
+ $structure['current'] = \true;
246
+ $structure['satisfied'] = \true;
247
+ $satisfied = \false;
248
+ foreach (\explode('|', $module) as $moduleName) {
249
+ if (\in_array($moduleName, $modules)) {
250
+ $satisfied = \true;
251
+ break;
252
+ }
253
+ }
254
+ if (!$satisfied) {
255
+ $structure['satisfied'] = \false;
256
+ $structure['current'] = \false;
257
+ $this->satisfied = \false;
258
+ $structure['message'] = "Apache module `{$module}` is not loaded";
259
+ $this->errors[] = $structure['message'];
260
+ }
261
+ $values[$module] = $structure;
262
+ }
263
+ return $values;
264
+ }
265
+ /**
266
+ * Validates only the functions requirement
267
+ *
268
+ * @return array
269
+ */
270
+ public function validateFunctionRequirement()
271
+ {
272
+ $values = [];
273
+ foreach ($this->requirements['functions'] as $key => $func) {
274
+ $data = $this->getParsedStructure();
275
+ $data['preferred'] = $func;
276
+ $data['current'] = \false;
277
+ $satisfied = \false;
278
+ foreach (\explode('|', $func) as $function) {
279
+ if (\function_exists($function)) {
280
+ $satisfied = \true;
281
+ break;
282
+ }
283
+ }
284
+ if ($satisfied) {
285
+ $data['satisfied'] = \true;
286
+ $data['current'] = \true;
287
+ } else {
288
+ $this->satisfied = \false;
289
+ $data['message'] = "PHP function `{$func}()` is not defined";
290
+ $this->errors[] = $data['message'];
291
+ }
292
+ $values[$func] = $data;
293
+ }
294
+ return $values;
295
+ }
296
+ /**
297
+ * Validates only the extensions requirement
298
+ *
299
+ * @return array
300
+ */
301
+ public function validateExtensionRequirement()
302
+ {
303
+ $values = [];
304
+ foreach ($this->requirements['extensions'] as $key => $ext) {
305
+ $data = $this->getParsedStructure();
306
+ $data['preferred'] = $ext;
307
+ $data['current'] = \false;
308
+ $satisfied = \false;
309
+ foreach (\explode('|', $ext) as $extension) {
310
+ if (\extension_loaded($extension)) {
311
+ $satisfied = \true;
312
+ break;
313
+ }
314
+ }
315
+ if ($satisfied) {
316
+ $data['satisfied'] = \true;
317
+ $data['current'] = \true;
318
+ } else {
319
+ $this->satisfied = \false;
320
+ $data['message'] = "PHP extension {$ext} is not loaded";
321
+ $this->errors[] = $data['message'];
322
+ }
323
+ $values[$ext] = $data;
324
+ }
325
+ return $values;
326
+ }
327
+ /**
328
+ * Validate only the system requirements, includes the php version, OS and apache version
329
+ *
330
+ * @return array
331
+ */
332
+ public function validateSystemRequirement()
333
+ {
334
+ $values = [];
335
+ if ($this->requirements['system']['php_version']) {
336
+ $structure = $this->getParsedStructure();
337
+ $structure['current'] = \PHP_VERSION;
338
+ $structure['preferred'] = $this->requirements['system']['php_version'];
339
+ $parsed = $this->parseComparisonString($this->requirements['system']['php_version'], '>=');
340
+ $result = \version_compare(\PHP_VERSION, $parsed['plain'], $parsed['operator']);
341
+ $structure['satisfied'] = $result;
342
+ if (!$result) {
343
+ $this->satisfied = \false;
344
+ $structure['message'] = \sprintf('PHP version must be %1$s %2$s', $this->locale[$parsed['operator']], $structure['preferred']);
345
+ $this->errors[] = $structure['message'];
346
+ }
347
+ $values['php_version'] = $structure;
348
+ }
349
+ // Now the OS
350
+ if ($this->requirements['system']['os']) {
351
+ $structureOS = $this->getParsedStructure();
352
+ $os = \DIRECTORY_SEPARATOR === '\\' ? static::OS_DOS : static::OS_UNIX;
353
+ $structureOS['satisfied'] = \true;
354
+ $structureOS['preferred'] = $this->requirements['system']['os'];
355
+ $structureOS['current'] = $os;
356
+ if ($os !== $structureOS['preferred']) {
357
+ $structureOS['satisfied'] = \false;
358
+ $this->satisfied = \false;
359
+ $structureOS['message'] = "The operating system must be {$structureOS['preferred']}, currently we are on a {$os} system";
360
+ $this->errors[] = $structureOS['message'];
361
+ }
362
+ $values['os'] = $structureOS;
363
+ }
364
+ return $values;
365
+ }
366
+ /**
367
+ * Validates only the file requirements
368
+ *
369
+ * @return array
370
+ */
371
+ public function validateFileRequirement()
372
+ {
373
+ $values = [];
374
+ foreach ($this->requirements['files'] as $file => $checks) {
375
+ $structure = $this->getParsedStructure();
376
+ $file = $this->unixPath($file);
377
+ $structure['path'] = $file;
378
+ $type = 'path';
379
+ $exists = \file_exists($file);
380
+ if (\is_file($file)) {
381
+ $type = 'file';
382
+ } elseif (\is_dir($file)) {
383
+ $type = 'directory';
384
+ }
385
+ foreach ($checks as $check) {
386
+ $data = $structure;
387
+ $data['preferred'] = $check;
388
+ $data['satisfied'] = (bool) $check($file);
389
+ $data['current'] = $data['satisfied'];
390
+ if (!$data['satisfied']) {
391
+ $this->satisfied = \false;
392
+ switch ($check) {
393
+ case static::CHECK_IS_DIR:
394
+ $data['message'] = "The path `{$file}` must be a directory";
395
+ if (!$exists) {
396
+ $data['message'] .= ", but the path doesn't even exist";
397
+ } elseif ($type === 'file') {
398
+ $data['message'] .= ', but the path is a file';
399
+ }
400
+ $data['current'] = "No directory";
401
+ break;
402
+ case static::CHECK_IS_FILE:
403
+ $data['message'] = "The path `{$file}` must be a file";
404
+ $data['current'] = "No file";
405
+ if (!$exists) {
406
+ $data['message'] .= ", but the path doesn't even exist";
407
+ } elseif ($type === 'directory') {
408
+ $data['message'] .= ', but the path is a directory';
409
+ }
410
+ break;
411
+ case static::CHECK_FILE_EXISTS:
412
+ $data['message'] = "The path `{$file}` doesn't exist";
413
+ $data['current'] = "Doesn't exist";
414
+ break;
415
+ case static::CHECK_IS_READABLE:
416
+ case static::CHECK_IS_WRITABLE:
417
+ $humanFriendly = \str_replace('is_', '', $check);
418
+ $data['current'] = "Not {$humanFriendly}";
419
+ $data['message'] = "The path `{$file}` must be {$humanFriendly}";
420
+ if (!$exists) {
421
+ $data['message'] .= ", but the path doesn't even exist";
422
+ }
423
+ break;
424
+ }
425
+ $this->errors[] = $data['message'];
426
+ }
427
+ $values[] = $data;
428
+ }
429
+ }
430
+ return $values;
431
+ }
432
+ /**
433
+ * Require a certain OS
434
+ *
435
+ * @param string $os The OS would pass the test
436
+ * Possible values: DOS, WIN
437
+ * @return Checker
438
+ */
439
+ public function requireOS($os)
440
+ {
441
+ $this->requirements['system']['os'] = $os;
442
+ return $this;
443
+ }
444
+ /**
445
+ * Require php.ini config values
446
+ *
447
+ * @param array $values As key => value format
448
+ * @return Checker
449
+ */
450
+ public function requireIniValues(array $values)
451
+ {
452
+ foreach ($values as $key => $value) {
453
+ $this->requirements['ini_values'][$key] = $value;
454
+ }
455
+ return $this;
456
+ }
457
+ /**
458
+ * Set required PHP version
459
+ *
460
+ * @param string $version The required PHP version
461
+ * @return Checker
462
+ */
463
+ public function requirePhpVersion($version)
464
+ {
465
+ $this->requirements['system']['php_version'] = $version;
466
+ return $this;
467
+ }
468
+ /**
469
+ * Add required extensions
470
+ *
471
+ * @param array|string $extensions The exact name(s) of the extension as they appear of the phpinfo() page
472
+ * @return Checker
473
+ */
474
+ public function requirePhpExtensions(array $extensions)
475
+ {
476
+ foreach ($extensions as $ext) {
477
+ $this->requirements['extensions'][$ext] = $ext;
478
+ }
479
+ return $this;
480
+ }
481
+ /**
482
+ * Add required functions(s)
483
+ *
484
+ * @param array $functions Required function(s) name
485
+ * @return Checker
486
+ */
487
+ public function requireFunctions(array $functions)
488
+ {
489
+ foreach ($functions as $func) {
490
+ $this->requirements['functions'][$func] = $func;
491
+ }
492
+ return $this;
493
+ }
494
+ /**
495
+ * Add required classe(es)
496
+ *
497
+ * @param array $classes Required class(es) name
498
+ * @return Checker
499
+ */
500
+ public function requireClasses(array $classes)
501
+ {
502
+ foreach ($classes as $class) {
503
+ $this->requirements['classes'][$class] = $class;
504
+ }
505
+ return $this;
506
+ }
507
+ /**
508
+ * Require list of apache modules (check will be performed only if server is apache)
509
+ *
510
+ * @param array $modules
511
+ * @return Checker
512
+ */
513
+ public function requireApacheModules(array $modules)
514
+ {
515
+ foreach ($modules as $module) {
516
+ $this->requirements['apache_modules'][$module] = $module;
517
+ }
518
+ return $this;
519
+ }
520
+ /**
521
+ * Require a file or folder with appropriate check
522
+ *
523
+ * @param string $path Path to the file/directory
524
+ * @param string $check Any of the supported checks, defaults to file_exists
525
+ * @return Checker
526
+ */
527
+ public function requireFile($path, $check = self::CHECK_FILE_EXISTS)
528
+ {
529
+ $supportedChecks = ['is_file', 'is_dir', 'is_readable', 'is_writable', 'file_exists'];
530
+ if (!\in_array($check, $supportedChecks)) {
531
+ throw new \InvalidArgumentException("No such check is supported!");
532
+ }
533
+ $this->requirements['files'][$path][] = $check;
534
+ return $this;
535
+ }
536
+ /**
537
+ * Alias of $this->requireFile() for people who are extra concerned about verbosity
538
+ *
539
+ * @param string $path Path to the file/directory
540
+ * @param string $check Any of the supported checks, defaults to file_exists
541
+ * @return Checker
542
+ */
543
+ public function requireDirectory($path, $check = self::CHECK_FILE_EXISTS)
544
+ {
545
+ return $this->requireFile($path, $check);
546
+ }
547
+ /**
548
+ * Remove extensions requirements
549
+ *
550
+ * @param array $keys extensions to remove as an array
551
+ * @return Checker
552
+ */
553
+ public function removeExtensionsRequirement(array $keys)
554
+ {
555
+ foreach ($keys as $key) {
556
+ unset($this->requirements['extensions'][$key]);
557
+ }
558
+ return $this;
559
+ }
560
+ /**
561
+ * Remove apache modules requirements
562
+ *
563
+ * @param array $keys apache modules to remove as an array
564
+ * @return Checker
565
+ */
566
+ public function removeApacheModulesRequirement(array $keys)
567
+ {
568
+ foreach ($keys as $key) {
569
+ unset($this->requirements['apache_modules'][$key]);
570
+ }
571
+ return $this;
572
+ }
573
+ /**
574
+ * Remove classes requirements
575
+ *
576
+ * @param array $keys classes to remove as an array
577
+ * @return Checker
578
+ */
579
+ public function removeClassesRequirement(array $keys)
580
+ {
581
+ foreach ($keys as $key) {
582
+ unset($this->requirements['classes'][$key]);
583
+ }
584
+ return $this;
585
+ }
586
+ /**
587
+ * Remove functions requirements
588
+ *
589
+ * @param array $keys functions to remove as an array
590
+ * @return Checker
591
+ */
592
+ public function removeFunctionsRequirement(array $keys)
593
+ {
594
+ foreach ($keys as $key) {
595
+ unset($this->requirements['functions'][$key]);
596
+ }
597
+ return $this;
598
+ }
599
+ /**
600
+ * Remove INI requirements
601
+ *
602
+ * @param array $keys Keys as an array
603
+ * @return Checker
604
+ */
605
+ public function removeIniValuesRequirement(array $keys)
606
+ {
607
+ foreach ($keys as $key) {
608
+ unset($this->requirements['ini_values'][$key]);
609
+ }
610
+ return $this;
611
+ }
612
+ /**
613
+ * Remove a file/directory requirement
614
+ *
615
+ * @param string $path The file path that you added earlier using self::requireFile()
616
+ * @param string|null The check name to remove, set this to null/empty and
617
+ * the entire path will be removed with all the checks.
618
+ * @return Checker
619
+ */
620
+ public function removeFilesRequirement($path, $check = null)
621
+ {
622
+ // Make sure the path is added
623
+ if (!isset($this->requirements['files'][$path])) {
624
+ return $this;
625
+ }
626
+ // No check passed so just remove the file entirely
627
+ if (!$check) {
628
+ unset($this->requirements['files'][$path]);
629
+ return $this;
630
+ }
631
+ $newVals = $this->arrayRemoveValue($this->requirements['files'][$path], [$check]);
632
+ $this->requirements['files'][$path] = $newVals;
633
+ return $this;
634
+ }
635
+ /**
636
+ * Remove currently set OS requirements
637
+ *
638
+ * @return Checker
639
+ */
640
+ public function removeOsRequirement()
641
+ {
642
+ $this->requirements['system']['os'] = null;
643
+ return $this;
644
+ }
645
+ /**
646
+ * Remove PHP version requirement
647
+ *
648
+ * @return Checker
649
+ */
650
+ public function removePhpVersionRequirement()
651
+ {
652
+ $this->requirements['system']['php_version'] = null;
653
+ return $this;
654
+ }
655
+ /**
656
+ * Remove specific value from one-dimensional array
657
+ *
658
+ * @param array $array The array to remove values from
659
+ * @param array $removals List of values to remove
660
+ *
661
+ * @return array
662
+ */
663
+ public function arrayRemoveValue(array $array, array $removals)
664
+ {
665
+ return \array_diff($array, $removals);
666
+ }
667
+ /**
668
+ * Transform any file path to unix style path
669
+ *
670
+ * @param string $string
671
+ * @return string
672
+ */
673
+ public function unixPath($string)
674
+ {
675
+ return \str_replace('\\', '/', $string);
676
+ }
677
+ /**
678
+ * Checks if a string is PHP style namespaced using backslash or not
679
+ *
680
+ * @param string $string
681
+ * @return boolean
682
+ */
683
+ public function ensureNamespace($string)
684
+ {
685
+ // Already namespaced
686
+ if (\strpos($string, '\\') !== \false) {
687
+ return $string;
688
+ }
689
+ // Append a global namespace
690
+ return '\\' . $string;
691
+ }
692
+ /**
693
+ * Parse a string that has comparison operator prepended to it
694
+ *
695
+ * @param string $string
696
+ * @param string $default
697
+ * @return array As operator => the comparison operator if exists
698
+ * plain => the string without the operator
699
+ **/
700
+ public function parseComparisonString($string, $default)
701
+ {
702
+ $comparison = \substr($string, 0, 1);
703
+ $comparison2 = \substr($string, 0, 2);
704
+ $operator = $default;
705
+ $withoutVersion = $string;
706
+ if ($comparison2 == '>=' || $comparison2 == '<=') {
707
+ $operator = $comparison2;
708
+ $withoutVersion = \substr($string, 2);
709
+ } elseif ($comparison == '>' || $comparison == '<' || $comparison == '=') {
710
+ $operator = $comparison;
711
+ $withoutVersion = \substr($string, 1);
712
+ }
713
+ return ['operator' => $operator, 'plain' => $withoutVersion];
714
+ }
715
+ /**
716
+ * Loosely compare two variables with the comparison operator provided
717
+ *
718
+ * @param mixed $var1
719
+ * @param string $op
720
+ * @param mixed $var2
721
+ * @return boolean
722
+ */
723
+ public function looseComparison($var1, $op, $var2)
724
+ {
725
+ switch ($op) {
726
+ case "=":
727
+ return $var1 == $var2;
728
+ case "!=":
729
+ return $var1 != $var2;
730
+ case ">=":
731
+ return $var1 >= $var2;
732
+ case "<=":
733
+ return $var1 <= $var2;
734
+ case ">":
735
+ return $var1 > $var2;
736
+ case "<":
737
+ return $var1 < $var2;
738
+ default:
739
+ return \true;
740
+ }
741
+ }
742
+ /**
743
+ * Returns bytes from php.ini string values
744
+ *
745
+ * @param string $val
746
+ * @return integer
747
+ */
748
+ public function returnBytes($val)
749
+ {
750
+ $val = \strtolower(\trim($val));
751
+ if (\substr($val, -1) == 'b') {
752
+ $val = \substr($val, 0, -1);
753
+ }
754
+ $last = \substr($val, -1);
755
+ $val = \intval($val);
756
+ switch ($last) {
757
+ case 'g':
758
+ case 'gb':
759
+ $val *= 1024;
760
+ // no break
761
+ case 'm':
762
+ case 'mb':
763
+ $val *= 1024;
764
+ // no break
765
+ case 'k':
766
+ case 'kb':
767
+ $val *= 1024;
768
+ }
769
+ return $val;
770
+ }
771
+ /**
772
+ * Returns base structure for the values
773
+ *
774
+ * @return array
775
+ */
776
+ protected function getParsedStructure()
777
+ {
778
+ return ['satisfied' => \false, 'preferred' => null, 'current' => null, 'message' => null];
779
+ }
780
+ }
third-party/vendor/mirazmac/php-requirements-checker/usage/usage.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WSAL_Vendor;
4
+
5
+ require '../vendor/autoload.php';
6
+ use WSAL_Vendor\MirazMac\Requirements\Checker;
7
+ $checker = new Checker();
8
+ // Define requirements
9
+ // Make sure the PHP version is equal to or greater than 5.6
10
+ // Pass preferred php.ini values as an array
11
+ // Note the usage of boolean instead of On/1/Off/0
12
+ // Ensures allow_url_fopen is On
13
+ $checker->requirePhpVersion('>=5.6')->requirePhpExtensions(['pdo', 'mbstring'])->requireFunctions(['random_bytes'])->requireFile('../composer.json', Checker::CHECK_FILE_EXISTS)->requireDirectory('../src', Checker::CHECK_IS_READABLE)->requireIniValues(['allow_url_fopen' => \true, 'short_open_tag' => \true, 'memory_limit' => '>=64M']);
14
+ // Runs the check and returns parsed requirements as an array
15
+ // Contains parsed requirements with state of the current values and their comparison result
16
+ $output = $checker->check();
17
+ // Should be called after running check() to see if requirements has met or not
18
+ $satisfied = $checker->isSatisfied();
19
+ if ($satisfied) {
20
+ echo "Requirements are met.";
21
+ } else {
22
+ echo \join(', ', $checker->getErrors());
23
+ }
third-party/vendor/scoper-autoload.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // scoper-autoload.php @generated by PhpScoper
4
+
5
+ $loader = require_once __DIR__.'/autoload.php';
6
+
7
+ return $loader;
wp-security-audit-log.php CHANGED
@@ -1,38 +1,42 @@
1
  <?php
2
  /**
 
 
 
 
 
 
3
  * Plugin Name: WP Activity Log
4
- * Plugin URI: https://wpactivitylog.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 Activity Log generates a security alert for everything that happens on your WordPress blogs and websites. Use the Activity log viewer included in the plugin to see all the security alerts.
6
- * Author: WP White Security
7
- * Version: 4.3.6
 
8
  * Text Domain: wp-security-audit-log
9
- * Author URI: https://www.wpwhitesecurity.com/
10
- * License: GPL2
 
 
11
  * Network: true
12
  *
13
  * @package wsal
14
  *
15
- * @fs_premium_only /extensions/, /sdk/twilio-php/
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  */
17
 
18
- /*
19
- WP Activity Log
20
- Copyright(c) 2021 WP White Security (email : info@wpwhitesecurity.com)
21
-
22
- This program is free software; you can redistribute it and/or modify
23
- it under the terms of the GNU General Public License, version 2, as
24
- published by the Free Software Foundation.
25
-
26
- This program is distributed in the hope that it will be useful,
27
- but WITHOUT ANY WARRANTY; without even the implied warranty of
28
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29
- GNU General Public License for more details.
30
-
31
- You should have received a copy of the GNU General Public License
32
- along with this program; if not, write to the Free Software
33
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
34
- */
35
-
36
  if ( ! function_exists( 'wsal_freemius' ) ) {
37
 
38
  if ( ! class_exists( 'WpSecurityAuditLog' ) ) {
@@ -49,9 +53,9 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
49
  *
50
  * @var string
51
  */
52
- public $version = '4.3.6';
53
 
54
- /**
55
  * Plugin constants.
56
  *
57
  * @var string
@@ -467,6 +471,7 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
467
  require_once 'classes/ThirdPartyExtensions/WooCommerceExtension.php';
468
  require_once 'classes/ThirdPartyExtensions/GravityFormsExtension.php';
469
  require_once 'classes/ThirdPartyExtensions/TablePressExtension.php';
 
470
  }
471
 
472
  // Connectors.
@@ -540,7 +545,7 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
540
  wp_schedule_event( time(), 'daily', 'wsal_delete_logins' );
541
  }
542
 
543
- add_filter( 'mainwp_child_extra_execution', array( $this, 'mainwp_dashboard_callback' ), 10, 2 );
544
 
545
  add_action( 'admin_init', array( $this, 'maybe_sync_premium_freemius' ) );
546
 
@@ -557,9 +562,8 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
557
  $bbpress_addon = new WSAL_BBPressExtension();
558
  $wpforms_addon = new WSAL_WPFormsExtension();
559
  $gravityforms_addon = new WSAL_GravityFormsExtension();
560
-
561
- // Comment out untill release.
562
- //$tablepress_addon = new WSAL_TablePressExtension();
563
  }
564
 
565
  // Extensions which are both admin and frontend based.
@@ -751,133 +755,6 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
751
  return ! in_array( $alert, $alerts );
752
  }
753
 
754
- /**
755
- * MainWP Dashboard Handler.
756
- *
757
- * @since 3.2.5
758
- *
759
- * @param array $info – Information to return.
760
- * @param array $post_data – Post data array from MainWP.
761
- * @return mixed
762
- */
763
- public function mainwp_dashboard_callback( $info, $post_data ) {
764
- if ( isset( $post_data['action'] ) ) {
765
- switch ( $post_data['action'] ) {
766
- case 'check_wsal':
767
- $info = new stdClass();
768
- $info->wsal_installed = true;
769
- $info->is_premium = false;
770
- break;
771
-
772
- case 'get_events':
773
- $limit = isset( $post_data['events_count'] ) ? $post_data['events_count'] : false;
774
- $offset = isset( $post_data['events_offset'] ) ? $post_data['events_offset'] : false;
775
- $query_args = isset( $post_data['query_args'] ) ? $post_data['query_args'] : false;
776
- $info = $this->alerts->get_mainwp_extension_events( $limit, $offset, $query_args );
777
- break;
778
-
779
- case 'get_report':
780
- $filters = isset( $post_data['filters'] ) ? $post_data['filters'] : array();
781
- $report_type = isset( $post_data['report_type'] ) ? $post_data['report_type'] : false;
782
- $info = $this->alerts->get_mainwp_extension_report( $filters, $report_type );
783
- break;
784
-
785
- case 'latest_event':
786
- // run the query and return it.
787
- $event = $this->query_for_latest_event();
788
- $event = $event->getAdapter()->Execute( $event );
789
-
790
- // Set the return object.
791
- if ( isset( $event[0] ) ) {
792
- $info = new stdClass();
793
- $info->alert_id = $event[0]->alert_id;
794
- $info->created_on = $event[0]->created_on;
795
- } else {
796
- $info = false;
797
- }
798
- break;
799
- case 'enforce_settings':
800
- // check subaction
801
- if ( ! array_key_exists( 'subaction', $post_data) || empty( $post_data['subaction'] ) ) {
802
- $info = array(
803
- 'success' => 'no',
804
- 'message' => 'Missing subaction parameter.'
805
- );
806
- break;
807
- }
808
-
809
- $subaction = filter_var( $post_data['subaction'], FILTER_SANITIZE_STRING);
810
- if ( ! in_array( $subaction, [ 'update', 'remove' ] ) ) {
811
- $info = array(
812
- 'success' => 'no',
813
- 'message' => 'Unsupported subaction parameter value.'
814
- );
815
- break;
816
- }
817
-
818
- if ( 'update' === $subaction ) {
819
- // store the enforced settings in local database (used for example to disable related parts
820
- // of the settings UI
821
- $settings_to_enforce = $post_data[ 'settings'];
822
- $this->settings()->set_mainwp_enforced_settings( $settings_to_enforce );
823
-
824
- // change the existing settings
825
- if ( array_key_exists( 'pruning_enabled', $settings_to_enforce ) ) {
826
- $this->settings()->SetPruningDateEnabled( $settings_to_enforce['pruning_enabled'] );
827
- if ( array_key_exists( 'pruning_date', $settings_to_enforce ) && array_key_exists( 'pruning_unit', $settings_to_enforce) ) {
828
- $this->settings()->SetPruningDate($settings_to_enforce[ 'pruning_date' ] . ' ' . $settings_to_enforce[ 'pruning_unit' ]);
829
- $this->settings()->set_pruning_unit( $settings_to_enforce[ 'pruning_unit' ] );
830
- }
831
- }
832
-
833
- if ( array_key_exists( 'disabled_events', $settings_to_enforce ) ) {
834
- $disabled_event_ids = array_key_exists( 'disabled_events', $settings_to_enforce ) ? array_map( 'intval', explode( ',', $settings_to_enforce['disabled_events'] ) ) : [];
835
- $this->alerts->SetDisabledAlerts( $disabled_event_ids );
836
- }
837
-
838
- if (array_key_exists('incognito_mode_enabled', $settings_to_enforce)) {
839
- $this->settings()->SetIncognito($settings_to_enforce['incognito_mode_enabled']);
840
- }
841
-
842
- if (array_key_exists('login_notification_enabled', $settings_to_enforce)) {
843
- $login_page_notification_enabled = $settings_to_enforce['login_notification_enabled'];
844
- $this->settings()->set_login_page_notification($login_page_notification_enabled);
845
- if ('yes' === $login_page_notification_enabled) {
846
- $this->settings()->set_login_page_notification_text($settings_to_enforce['login_notification_text']);
847
- }
848
- }
849
-
850
- } else if ( 'remove' === $subaction ) {
851
- $this->settings()->delete_mainwp_enforced_settings();
852
- }
853
-
854
- $info = array(
855
- 'success' => 'yes'
856
- );
857
- $this->alerts->Trigger( 6043 );
858
- default:
859
- break;
860
- }
861
- }
862
- return $info;
863
- }
864
-
865
- /**
866
- * Performs a query to retrieve the latest event in the logs.
867
- *
868
- * @method query_for_latest_event
869
- * @since 4.0.3
870
- * @return array
871
- */
872
- public function query_for_latest_event() {
873
- $event_query = new WSAL_Models_OccurrenceQuery();
874
- // order by creation.
875
- $event_query->addOrderBy( 'created_on', true );
876
- // only request 1 item.
877
- $event_query->setLimit( 1 );
878
- return $event_query;
879
- }
880
-
881
  /**
882
  * Method: WSAL plugin redirect.
883
  */
@@ -1073,27 +950,33 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1073
  );
1074
  }
1075
 
1076
- /**
1077
- * Limited License Activation Error.
1078
- *
1079
- * @param string $error - Error Message.
1080
- * @return string
1081
- */
1082
- public function limited_license_activation_error( $error ) {
1083
- $site_count = null;
1084
- preg_match( '!\d+!', $error, $site_count );
1085
-
1086
- // Check if this is an expired error.
1087
- if ( strpos( $error, 'expired' ) !== false ) {
1088
- /* Translators: Expired message and time */
1089
- $error = sprintf( esc_html__( '%s You need to renew your license to continue using premium features.', 'wp-security-audit-log' ), preg_replace('/\([^)]+\)/','', $error ) );
1090
- }
1091
- elseif ( ! empty( $site_count[0] ) ) {
1092
- /* Translators: Number of sites */
1093
- $error = sprintf( esc_html__( 'The license is limited to %s sub-sites. You need to upgrade your license to cover all the sub-sites on this network.', 'wp-security-audit-log' ), $site_count[0] );
1094
- }
1095
- return $error;
1096
- }
 
 
 
 
 
 
1097
 
1098
  /**
1099
  * Start to trigger the events after installation.
@@ -1148,6 +1031,9 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1148
  */
1149
  do_action( 'wsal_init', $this );
1150
 
 
 
 
1151
  // allow registration of custom alert formatters (must be called after wsal_init action )
1152
  WSAL_AlertFormatterFactory::bootstrap();
1153
  }
@@ -1304,11 +1190,12 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1304
  $script_data = array(
1305
  'ajaxURL' => admin_url( 'admin-ajax.php' ),
1306
  'liveEvents' => $live_events_enabled,
1307
- 'installing' => __( 'Installing, please wait', 'wp-security-audit-log' ),
1308
- 'already_installed' => __( 'Already installed', 'wp-security-audit-log' ),
1309
- 'installed' => __( 'Extension installed', 'wp-security-audit-log' ),
1310
- 'activated' => __( 'Extension activated', 'wp-security-audit-log' ),
1311
- 'failed' => __( 'Install failed', 'wp-security-audit-log' ),
 
1312
  );
1313
 
1314
  wp_localize_script( 'wsal-common', 'wsalCommonData', $script_data );
@@ -1331,7 +1218,6 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1331
  require_once 'classes/ConstantManager.php';
1332
  require_once 'classes/Loggers/Database.php';
1333
  require_once 'classes/SensorManager.php';
1334
- require_once 'classes/Sensors/Public.php';
1335
  require_once 'classes/Settings.php';
1336
 
1337
  if ( is_admin() ) {
@@ -1436,7 +1322,7 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1436
  */
1437
  public function Update( $old_version, $new_version ) {
1438
  // Update version in db.
1439
- $this->SetGlobalSetting( 'version', $new_version );
1440
 
1441
  if ( '0.0.0' === $old_version ) {
1442
  // set some initial plugins settings (only the ones that bypass the regular settings retrieval at some
@@ -1536,6 +1422,34 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1536
  $this->DeleteGlobalSetting( 'excluded-custom' );
1537
  }
1538
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1539
  }
1540
  }
1541
 
@@ -1646,25 +1560,25 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1646
  $this->SetGlobalSetting( $option, $value );
1647
  }
1648
 
1649
- /**
1650
- * Set a global setting.
1651
- *
1652
- * @param string $option - Option name.
1653
- * @param mixed $value - New value for option.
1654
- *
1655
- * @return bool
1656
- */
1657
- public function SetGlobalSetting( $option, $value ) {
1658
- $this->include_options_helper();
1659
- return $this->options_helper->set_option_value( $option, $value );
1660
- }
 
 
1661
 
1662
  /**
1663
  * Deletes a global setting.
1664
  *
1665
- * Handles option names without the prefix, but also the ones that do for backwards compatibility.
1666
- *
1667
- * @param string $option - Option name.
1668
  *
1669
  * @return bool
1670
  * @since 4.2.1
@@ -1687,17 +1601,20 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1687
  $result = $this->GetGlobalSetting( $option, \WSAL\Helpers\Options::string_to_bool( $default ) );
1688
  return \WSAL\Helpers\Options::string_to_bool( $result );
1689
  }
1690
- /**
1691
- * Sets a global boolean setting. It takes care of the conversion between string and boolean.
1692
- *
1693
- * @param string $option - Option name.
1694
- * @param mixed $value - New value for option.
1695
- * @since 4.1.3
1696
- */
1697
- public function SetGlobalBooleanSetting( $option, $value ) {
1698
- $boolean_value = \WSAL\Helpers\Options::string_to_bool( $value );
1699
- $this->SetGlobalSetting( $option, \WSAL\Helpers\Options::bool_to_string( $boolean_value ) );
1700
- }
 
 
 
1701
 
1702
  /**
1703
  * Run cleanup routines.
@@ -1951,7 +1868,7 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
1951
  $old_value = get_option( $option_name );
1952
 
1953
  // determine new value via Freemius SDK
1954
- $new_value = wsal_freemius()->is_registered() && wsal_freemius()->has_active_valid_license() ? 'yes' : 'no';
1955
 
1956
  // update the db option only if the value changed
1957
  if ($new_value != $old_value) {
@@ -2197,6 +2114,17 @@ if ( ! function_exists( 'wsal_freemius' ) ) {
2197
  }
2198
  }
2199
 
 
 
 
 
 
 
 
 
 
 
 
2200
  // Begin load sequence.
2201
  WpSecurityAuditLog::GetInstance();
2202
 
1
  <?php
2
  /**
3
+ * WP Activity Log.
4
+ *
5
+ * @copyright Copyright (C) 2013-@current_year, WP White Security - support@wpwhitesecurity.com
6
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 or higher
7
+ *
8
+ * @wordpress-plugin
9
  * Plugin Name: WP Activity Log
10
+ * Version: 4.4.0
11
+ * Plugin URI: https://wpactivitylog.com/
12
+ * Description: Identify WordPress security issues before they become a problem. Keep track of everything happening on your WordPress, including users activity. Similar to Linux Syslog, WP Activity Log generates an activity log with a record of everything that happens on your WordPress websites.
13
+ * Author: WP White Security
14
+ * Author URI: https://www.wpwhitesecurity.com/
15
  * Text Domain: wp-security-audit-log
16
+ * Domain Path: /languages/
17
+ * License: GPL v3
18
+ * Requires at least: 5.0
19
+ * Requires PHP: 7.0
20
  * Network: true
21
  *
22
  * @package wsal
23
  *
24
+ * @fs_premium_only /extensions/, /third-party/woocommerce/
25
+ *
26
+ * This program is free software: you can redistribute it and/or modify
27
+ * it under the terms of the GNU General Public License as published by
28
+ * the Free Software Foundation, either version 3 of the License, or
29
+ * (at your option) any later version.
30
+ *
31
+ * This program is distributed in the hope that it will be useful,
32
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
33
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34
+ * GNU General Public License for more details.
35
+ *
36
+ * You should have received a copy of the GNU General Public License
37
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
38
  */
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  if ( ! function_exists( 'wsal_freemius' ) ) {
41
 
42
  if ( ! class_exists( 'WpSecurityAuditLog' ) ) {
53
  *
54
  * @var string
55
  */
56
+ public $version = '4.4.0';
57
 
58
+ /**
59
  * Plugin constants.
60
  *
61
  * @var string
471
  require_once 'classes/ThirdPartyExtensions/WooCommerceExtension.php';
472
  require_once 'classes/ThirdPartyExtensions/GravityFormsExtension.php';
473
  require_once 'classes/ThirdPartyExtensions/TablePressExtension.php';
474
+ require_once 'classes/ThirdPartyExtensions/WFCMExtension.php';
475
  }
476
 
477
  // Connectors.
545
  wp_schedule_event( time(), 'daily', 'wsal_delete_logins' );
546
  }
547
 
548
+ add_filter( 'mainwp_child_extra_execution', array( new WSAL_MainWpApi( $this ), 'handle_callback' ), 10, 2 );
549
 
550
  add_action( 'admin_init', array( $this, 'maybe_sync_premium_freemius' ) );
551
 
562
  $bbpress_addon = new WSAL_BBPressExtension();
563
  $wpforms_addon = new WSAL_WPFormsExtension();
564
  $gravityforms_addon = new WSAL_GravityFormsExtension();
565
+ $tablepress_addon = new WSAL_TablePressExtension();
566
+ $wfcm_addon = new WSAL_WFCMExtension();
 
567
  }
568
 
569
  // Extensions which are both admin and frontend based.
755
  return ! in_array( $alert, $alerts );
756
  }
757
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  /**
759
  * Method: WSAL plugin redirect.
760
  */
950
  );
951
  }
952
 
953
+ /**
954
+ * Limited License Activation Error.
955
+ *
956
+ * @param string $error - Error Message.
957
+ *
958
+ * @return string
959
+ */
960
+ public function limited_license_activation_error( $error ) {
961
+ // We only process error if it's some sort of string message.
962
+ if ( ! is_string( $error ) ) {
963
+ return $error;
964
+ }
965
+
966
+ $site_count = null;
967
+ preg_match( '!\d+!', $error, $site_count );
968
+
969
+ // Check if this is an expired error.
970
+ if ( strpos( $error, 'expired' ) !== false ) {
971
+ /* Translators: Expired message and time */
972
+ $error = sprintf( esc_html__( '%s You need to renew your license to continue using premium features.', 'wp-security-audit-log' ), preg_replace( '/\([^)]+\)/', '', $error ) );
973
+ } elseif ( ! empty( $site_count[0] ) ) {
974
+ /* Translators: Number of sites */
975
+ $error = sprintf( esc_html__( 'The license is limited to %s sub-sites. You need to upgrade your license to cover all the sub-sites on this network.', 'wp-security-audit-log' ), $site_count[0] );
976
+ }
977
+
978
+ return $error;
979
+ }
980
 
981
  /**
982
  * Start to trigger the events after installation.
1031
  */
1032
  do_action( 'wsal_init', $this );
1033
 
1034
+ // background job for metadata migration
1035
+ new WSAL_Upgrade_MetadataMigration();
1036
+
1037
  // allow registration of custom alert formatters (must be called after wsal_init action )
1038
  WSAL_AlertFormatterFactory::bootstrap();
1039
  }
1190
  $script_data = array(
1191
  'ajaxURL' => admin_url( 'admin-ajax.php' ),
1192
  'liveEvents' => $live_events_enabled,
1193
+ 'installing' => esc_html__( 'Installing, please wait', 'wp-security-audit-log' ),
1194
+ 'already_installed' => esc_html__( 'Already installed', 'wp-security-audit-log' ),
1195
+ 'installed' => esc_html__( 'Extension installed', 'wp-security-audit-log' ),
1196
+ 'activated' => esc_html__( 'Extension activated', 'wp-security-audit-log' ),
1197
+ 'failed' => esc_html__( 'Install failed', 'wp-security-audit-log' ),
1198
+ 'reloading_page' => esc_html__( 'Reloading page', 'wp-security-audit-log' )
1199
  );
1200
 
1201
  wp_localize_script( 'wsal-common', 'wsalCommonData', $script_data );
1218
  require_once 'classes/ConstantManager.php';
1219
  require_once 'classes/Loggers/Database.php';
1220
  require_once 'classes/SensorManager.php';
 
1221
  require_once 'classes/Settings.php';
1222
 
1223
  if ( is_admin() ) {
1322
  */
1323
  public function Update( $old_version, $new_version ) {
1324
  // Update version in db.
1325
+ $this->SetGlobalSetting( 'version', $new_version, true );
1326
 
1327
  if ( '0.0.0' === $old_version ) {
1328
  // set some initial plugins settings (only the ones that bypass the regular settings retrieval at some
1422
  $this->DeleteGlobalSetting( 'excluded-custom' );
1423
  }
1424
  }
1425
+
1426
+ if ( version_compare( $new_version, '4.4.0', '>=' ) ) {
1427
+ // Delete unwanted usermeta.
1428
+ global $wpdb;
1429
+ $all_user_meta = $wpdb->get_results(
1430
+ $wpdb->prepare(
1431
+ "DELETE FROM {$wpdb->usermeta} WHERE meta_key = '%s';",
1432
+ 'wsal-notice-update-44-notice'
1433
+ )
1434
+ );
1435
+
1436
+ $this->settings()->set_database_version( 44400 );
1437
+
1438
+ if ( class_exists( 'WSAL_Extension_Manager' ) ) {
1439
+ WSAL_Extension_Manager::include_extension( 'external-db' );
1440
+ }
1441
+
1442
+ if ( ! did_action( 'wsal_init' ) ) {
1443
+ // we need to call wsal init manually because it does not run as before the upgrade procedure is triggered
1444
+ do_action( 'wsal_init', $this );
1445
+ }
1446
+
1447
+ require_once 'classes/Upgrade/Upgrade_43000_to_44400.php';
1448
+ $upgrader = new WSAL_Upgrade_43000_to_44400( $this );
1449
+ $upgrader->run();
1450
+
1451
+ // @todo remove legacy periodic reports for unique_ip and number_logins
1452
+ }
1453
  }
1454
  }
1455
 
1560
  $this->SetGlobalSetting( $option, $value );
1561
  }
1562
 
1563
+ /**
1564
+ * Set a global setting.
1565
+ *
1566
+ * @param string $option - Option name.
1567
+ * @param mixed $value - New value for option.
1568
+ * @param bool $autoload Whether or not to autoload this option.
1569
+ *
1570
+ * @return bool
1571
+ */
1572
+ public function SetGlobalSetting( $option, $value, $autoload = false ) {
1573
+ $this->include_options_helper();
1574
+
1575
+ return $this->options_helper->set_option_value( $option, $value, $autoload );
1576
+ }
1577
 
1578
  /**
1579
  * Deletes a global setting.
1580
  *
1581
+ * @param string $option - Option name without the prefix.
 
 
1582
  *
1583
  * @return bool
1584
  * @since 4.2.1
1601
  $result = $this->GetGlobalSetting( $option, \WSAL\Helpers\Options::string_to_bool( $default ) );
1602
  return \WSAL\Helpers\Options::string_to_bool( $result );
1603
  }
1604
+
1605
+ /**
1606
+ * Sets a global boolean setting. It takes care of the conversion between string and boolean.
1607
+ *
1608
+ * @param string $option - Option name.
1609
+ * @param mixed $value - New value for option.
1610
+ * @param bool $autoload Whether or not to autoload this option.
1611
+ *
1612
+ * @since 4.1.3
1613
+ */
1614
+ public function SetGlobalBooleanSetting( $option, $value, $autoload = false ) {
1615
+ $boolean_value = \WSAL\Helpers\Options::string_to_bool( $value );
1616
+ $this->SetGlobalSetting( $option, \WSAL\Helpers\Options::bool_to_string( $boolean_value ), $autoload );
1617
+ }
1618
 
1619
  /**
1620
  * Run cleanup routines.
1868
  $old_value = get_option( $option_name );
1869
 
1870
  // determine new value via Freemius SDK
1871
+ $new_value = wsal_freemius()->has_active_valid_license() ? 'yes' : 'no';
1872
 
1873
  // update the db option only if the value changed
1874
  if ($new_value != $old_value) {
2114
  }
2115
  }
2116
 
2117
+ $prefixed_autoloader_file_path = plugin_dir_path( __FILE__ ) . implode( DIRECTORY_SEPARATOR, [
2118
+ 'third-party',
2119
+ 'vendor',
2120
+ 'autoload.php'
2121
+ ]
2122
+ );
2123
+
2124
+ if ( file_exists( $prefixed_autoloader_file_path ) ) {
2125
+ require_once $prefixed_autoloader_file_path;
2126
+ }
2127
+
2128
  // Begin load sequence.
2129
  WpSecurityAuditLog::GetInstance();
2130