WP Security Audit Log - Version 3.0.0

Version Description

Download this release

Release Info

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

Code changes from version 2.6.9.1 to 3.0.0

Files changed (67) hide show
  1. classes/AbstractLogger.php +30 -22
  2. classes/AbstractSandboxTask.php +98 -74
  3. classes/AbstractSensor.php +96 -63
  4. classes/AbstractView.php +240 -203
  5. classes/Adapters/ActiveRecordInterface.php +85 -13
  6. classes/Adapters/MetaInterface.php +30 -10
  7. classes/Adapters/MySQL/ActiveRecordAdapter.php +701 -669
  8. classes/Adapters/MySQL/MetaAdapter.php +130 -70
  9. classes/Adapters/MySQL/OccurrenceAdapter.php +307 -245
  10. classes/Adapters/MySQL/OptionAdapter.php +161 -108
  11. classes/Adapters/MySQL/QueryAdapter.php +259 -236
  12. classes/Adapters/MySQL/TmpUserAdapter.php +58 -32
  13. classes/Adapters/OccurrenceInterface.php +61 -9
  14. classes/Adapters/QueryInterface.php +36 -6
  15. classes/Alert.php +127 -113
  16. classes/AlertManager.php +165 -142
  17. classes/AuditLogListView.php +731 -568
  18. classes/Autoloader.php +87 -70
  19. classes/Connector/AbstractConnector.php +72 -35
  20. classes/Connector/ConnectorFactory.php +134 -102
  21. classes/Connector/ConnectorInterface.php +52 -10
  22. classes/Connector/MySQLDB.php +681 -666
  23. classes/Connector/wp-db-custom.php +51 -36
  24. classes/ConstantManager.php +98 -90
  25. classes/EDD_SL_Plugin_Updater.php +212 -164
  26. classes/Helpers/DataHelper.php +35 -22
  27. classes/LicenseManager.php +260 -188
  28. classes/Loggers/Database.php +217 -207
  29. classes/Models/ActiveRecord.php +382 -337
  30. classes/Models/Meta.php +92 -49
  31. classes/Models/Occurrence.php +287 -245
  32. classes/Models/OccurrenceQuery.php +52 -32
  33. classes/Models/Option.php +160 -121
  34. classes/Models/Query.php +334 -263
  35. classes/Models/TmpUser.php +36 -5
  36. classes/Nicer.php +300 -288
  37. classes/SensorManager.php +82 -69
  38. classes/Sensors/BBPress.php +615 -519
  39. classes/Sensors/Comments.php +200 -170
  40. classes/Sensors/Content.php +970 -647
  41. classes/Sensors/Database.php +193 -150
  42. classes/Sensors/Files.php +106 -76
  43. classes/Sensors/LogInOut.php +433 -370
  44. classes/Sensors/Menus.php +645 -514
  45. classes/Sensors/MetaData.php +271 -181
  46. classes/Sensors/Multisite.php +232 -183
  47. classes/Sensors/PhpErrors.php +166 -128
  48. classes/Sensors/PluginsThemes.php +424 -215
  49. classes/Sensors/Request.php +109 -68
  50. classes/Sensors/System.php +920 -724
  51. classes/Sensors/UserProfile.php +266 -184
  52. classes/Sensors/Widgets.php +289 -242
  53. classes/Sensors/WooCommerce.php +1346 -859
  54. classes/Settings.php +485 -463
  55. classes/SimpleProfiler.php +41 -47
  56. classes/ViewManager.php +309 -283
  57. classes/Views/About.php +0 -94
  58. classes/Views/AuditLog.php +178 -115
  59. classes/Views/EmailNotifications.php +167 -57
  60. classes/Views/Extensions.php +0 -137
  61. classes/Views/ExternalDB.php +166 -57
  62. classes/Views/Help.php +182 -79
  63. classes/Views/Licensing.php +155 -92
  64. classes/Views/LogInUsers.php +168 -56
  65. classes/Views/Reports.php +167 -56
  66. classes/Views/Search.php +160 -56
  67. classes/Views/Settings.php +240 -169
classes/AbstractLogger.php CHANGED
@@ -1,29 +1,37 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
- *
5
  * Abstract class used in the Logger.
 
6
  * @see Loggers/Database.php
 
7
  */
8
- abstract class WSAL_AbstractLogger
9
- {
10
- /**
11
- * @var WpSecurityAuditLog
12
- */
13
- protected $plugin;
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- public function __construct(WpSecurityAuditLog $plugin)
16
- {
17
- $this->plugin = $plugin;
18
- }
19
-
20
- /**
21
- * Log alert abstract.
22
- * @param integer $type alert code
23
- * @param array $data Metadata
24
- * @param integer $date (Optional) created_on
25
- * @param integer $siteid (Optional) site_id
26
- * @param bool $migrated (Optional) is_migrated
27
- */
28
- public abstract function Log($type, $data = array(), $date = null, $siteid = null, $migrated = false);
29
  }
1
  <?php
2
  /**
 
 
3
  * Abstract class used in the Logger.
4
+ *
5
  * @see Loggers/Database.php
6
+ * @package Wsal
7
  */
8
+ abstract class WSAL_AbstractLogger {
9
+
10
+ /**
11
+ * Instance of WpSecurityAuditLog.
12
+ *
13
+ * @var object
14
+ */
15
+ protected $plugin;
16
+
17
+ /**
18
+ * Method: Constructor.
19
+ *
20
+ * @param object $plugin - Instance of WpSecurityAuditLog.
21
+ * @since 1.0.0
22
+ */
23
+ public function __construct( WpSecurityAuditLog $plugin ) {
24
+ $this->plugin = $plugin;
25
+ }
26
 
27
+ /**
28
+ * Log alert abstract.
29
+ *
30
+ * @param integer $type - Alert code.
31
+ * @param array $data - Metadata.
32
+ * @param integer $date (Optional) - Created on.
33
+ * @param integer $siteid (Optional) - Site id.
34
+ * @param bool $migrated (Optional) - Is migrated.
35
+ */
36
+ public abstract function Log( $type, $data = array(), $date = null, $siteid = null, $migrated = false );
 
 
 
 
37
  }
classes/AbstractSandboxTask.php CHANGED
@@ -1,79 +1,103 @@
1
  <?php
2
  /**
 
 
3
  * @package Wsal
4
  */
5
- abstract class WSAL_AbstractSandboxTask
6
- {
7
- public function __construct()
8
- {
9
- // remove time limit and clear output buffers
10
- set_time_limit(0);
11
- ob_implicit_flush(true);
12
- while (ob_get_level()) {
13
- ob_end_flush();
14
- }
15
-
16
- // set up shutdown handler
17
- register_shutdown_function(array($this, 'Shutdown'));
18
-
19
- // run event sequence
20
- $this->Header();
21
- try {
22
- $this->Execute();
23
- } catch (Exception $ex) {
24
- $this->Message(get_class($ex) . ' [' . basename($ex->getFile()) . ':' . $ex->getLine() . ']: ' . $ex->getMessage());
25
- $this->Message($ex->getTraceAsString(), true);
26
- }
27
- $this->Footer();
28
-
29
- // shutdown
30
- die();
31
- }
32
-
33
- protected function Header()
34
- {
35
- echo '<!DOCTYPE html><html><body style="margin: 0; padding: 8px; font: 12px Arial; color: #333;">';
36
- echo '<div style="position: fixed; top: 0; left: 0; right: 0; padding: 8px; background: #F0F0F0;">';
37
- echo ' <div id="bar" style=" border-top: 2px solid #0AE; top: 20px; height: 0; width: 0%;"> </div>';
38
- echo ' <span id="msg"></span> <span id="prg"></span>';
39
- echo '</div>';
40
- echo '<div id="msgs" style="font-family: Consolas; margin-top: 30px; white-space: pre;"></div>';
41
- echo '<script>';
42
- echo ' var bar = document.getElementById("bar");';
43
- echo ' var msg = document.getElementById("msg");';
44
- echo ' var prg = document.getElementById("prg");';
45
- echo ' var msgs = document.getElementById("msgs");';
46
- echo '</script>';
47
- flush();
48
- }
49
-
50
- protected function Footer()
51
- {
52
- echo '<div style="display: none;">';
53
- flush();
54
- }
55
-
56
- protected abstract function Execute();
57
-
58
- public function Shutdown()
59
- {
60
- echo '</div></body></html>';
61
- flush();
62
- }
63
-
64
- protected function Progress($percent)
65
- {
66
- echo '<script>bar.style.width=prg.innerHTML="' . number_format($percent, 2) . '%";</script>';
67
- flush();
68
- }
69
-
70
- protected function Message($message, $sticky = false)
71
- {
72
- if ($sticky) {
73
- echo '<script>msgs.appendChild(document.createTextNode(' . json_encode($message . PHP_EOL) . ')); window.scroll(0, document.body.scrollHeight);</script>';
74
- } else {
75
- echo '<script>msg.innerHTML=' . json_encode($message) . ';</script>';
76
- }
77
- flush();
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
1
  <?php
2
  /**
3
+ * Abstract Sandbox Task class.
4
+ *
5
  * @package Wsal
6
  */
7
+ abstract class WSAL_AbstractSandboxTask {
8
+
9
+ /**
10
+ * Method: Constructor.
11
+ *
12
+ * @since 1.0.0
13
+ */
14
+ public function __construct() {
15
+ // Remove time limit and clear output buffers.
16
+ set_time_limit( 0 );
17
+ ob_implicit_flush( true );
18
+ while ( ob_get_level() ) {
19
+ ob_end_flush();
20
+ }
21
+
22
+ // Set up shutdown handler.
23
+ register_shutdown_function( array( $this, 'Shutdown' ) );
24
+
25
+ // Run event sequence.
26
+ $this->Header();
27
+ try {
28
+ $this->Execute();
29
+ } catch ( Exception $ex ) {
30
+ $this->Message( get_class( $ex ) . ' [' . basename( $ex->getFile() ) . ':' . $ex->getLine() . ']: ' . $ex->getMessage() );
31
+ $this->Message( $ex->getTraceAsString(), true );
32
+ }
33
+ $this->Footer();
34
+
35
+ // Shutdown.
36
+ die();
37
+ }
38
+
39
+ /**
40
+ * Header.
41
+ */
42
+ protected function Header() {
43
+ echo '<!DOCTYPE html><html><body style="margin: 0; padding: 8px; font: 12px Arial; color: #333;">';
44
+ echo '<div style="position: fixed; top: 0; left: 0; right: 0; padding: 8px; background: #F0F0F0;">';
45
+ echo ' <div id="bar" style=" border-top: 2px solid #0AE; top: 20px; height: 0; width: 0%;"> </div>';
46
+ echo ' <span id="msg"></span> <span id="prg"></span>';
47
+ echo '</div>';
48
+ echo '<div id="msgs" style="font-family: Consolas; margin-top: 30px; white-space: pre;"></div>';
49
+ echo '<script>';
50
+ echo ' var bar = document.getElementById("bar");';
51
+ echo ' var msg = document.getElementById("msg");';
52
+ echo ' var prg = document.getElementById("prg");';
53
+ echo ' var msgs = document.getElementById("msgs");';
54
+ echo '</script>';
55
+ flush();
56
+ }
57
+
58
+ /**
59
+ * Footer.
60
+ */
61
+ protected function Footer() {
62
+ echo '<div style="display: none;">';
63
+ flush();
64
+ }
65
+
66
+ /**
67
+ * Method: Execute.
68
+ */
69
+ protected abstract function Execute();
70
+
71
+ /**
72
+ * Method: Shutdown.
73
+ */
74
+ public function Shutdown() {
75
+ echo '</div></body></html>';
76
+ flush();
77
+ }
78
+
79
+ /**
80
+ * Method: Show progress.
81
+ *
82
+ * @param mix $percent - Progress percentage.
83
+ */
84
+ protected function Progress( $percent ) {
85
+ echo '<script>bar.style.width=prg.innerHTML="' . number_format( $percent, 2 ) . '%";</script>';
86
+ flush();
87
+ }
88
+
89
+ /**
90
+ * Method: Message.
91
+ *
92
+ * @param string $message - Message.
93
+ * @param bool $sticky - True if sticky.
94
+ */
95
+ protected function Message( $message, $sticky = false ) {
96
+ if ( $sticky ) {
97
+ echo '<script>msgs.appendChild(document.createTextNode(' . json_encode( $message . PHP_EOL ) . ')); window.scroll(0, document.body.scrollHeight);</script>';
98
+ } else {
99
+ echo '<script>msg.innerHTML=' . json_encode( $message ) . ';</script>';
100
+ }
101
+ flush();
102
+ }
103
  }
classes/AbstractSensor.php CHANGED
@@ -1,72 +1,105 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
- *
5
  * Abstract class used in all the sensors.
 
6
  * @see Sensors/*.php
 
7
  */
8
- abstract class WSAL_AbstractSensor
9
- {
10
- /**
11
- * @var WpSecurityAuditLog
12
- */
13
- protected $plugin;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- public function __construct(WpSecurityAuditLog $plugin)
16
- {
17
- $this->plugin = $plugin;
18
- }
 
 
 
 
 
19
 
20
- /**
21
- * @return boolean Whether we are running on multisite or not.
22
- */
23
- protected function IsMultisite()
24
- {
25
- return function_exists('is_multisite') && is_multisite();
26
- }
27
-
28
- abstract function HookEvents();
29
-
30
- protected function Log($type, $message, $args)
31
- {
32
- $this->plugin->alerts->Trigger($type, array(
33
- 'Message' => $message,
34
- 'Context' => $args,
35
- 'Trace' => debug_backtrace(),
36
- ));
37
- }
38
-
39
- protected function LogError($message, $args)
40
- {
41
- $this->Log(0001, $message, $args);
42
- }
43
-
44
- protected function LogWarn($message, $args)
45
- {
46
- $this->Log(0002, $message, $args);
47
- }
48
-
49
- protected function LogInfo($message, $args)
50
- {
51
- $this->Log(0003, $message, $args);
52
- }
53
 
54
- /**
55
- * Check to see whether or not the specified directory is accessible
56
- * @param string $dirPath
57
- * @return boolean
58
- */
59
- protected function CheckDirectory($dirPath)
60
- {
61
- if (!is_dir($dirPath)) {
62
- return false;
63
- }
64
- if (!is_readable($dirPath)) {
65
- return false;
66
- }
67
- if (!is_writable($dirPath)) {
68
- return false;
69
- }
70
- return true;
71
- }
72
  }
1
  <?php
2
  /**
 
 
3
  * Abstract class used in all the sensors.
4
+ *
5
  * @see Sensors/*.php
6
+ * @package Wsal
7
  */
8
+ abstract class WSAL_AbstractSensor {
9
+
10
+ /**
11
+ * Instance of WpSecurityAuditLog.
12
+ *
13
+ * @var object
14
+ */
15
+ protected $plugin;
16
+
17
+ /**
18
+ * Method: Constructor.
19
+ *
20
+ * @param object $plugin - Instance of WpSecurityAuditLog.
21
+ */
22
+ public function __construct( WpSecurityAuditLog $plugin ) {
23
+ $this->plugin = $plugin;
24
+ }
25
+
26
+ /**
27
+ * Whether we are running on multisite or not.
28
+ *
29
+ * @return boolean
30
+ */
31
+ protected function IsMultisite() {
32
+ return function_exists( 'is_multisite' ) && is_multisite();
33
+ }
34
+
35
+ /**
36
+ * Method: Hook events related to sensor.
37
+ */
38
+ abstract function HookEvents();
39
+
40
+ /**
41
+ * Method: Log the message for sensor.
42
+ *
43
+ * @param int $type - Type of alert.
44
+ * @param string $message - Alert message.
45
+ * @param mix $args - Message arguments.
46
+ */
47
+ protected function Log( $type, $message, $args ) {
48
+ $this->plugin->alerts->Trigger(
49
+ $type, array(
50
+ 'Message' => $message,
51
+ 'Context' => $args,
52
+ 'Trace' => debug_backtrace(),
53
+ )
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Method: Log error message for sensor.
59
+ *
60
+ * @param string $message - Alert message.
61
+ * @param mix $args - Message arguments.
62
+ */
63
+ protected function LogError( $message, $args ) {
64
+ $this->Log( 0001, $message, $args );
65
+ }
66
 
67
+ /**
68
+ * Method: Log warning message for sensor.
69
+ *
70
+ * @param string $message - Alert message.
71
+ * @param mix $args - Message arguments.
72
+ */
73
+ protected function LogWarn( $message, $args ) {
74
+ $this->Log( 0002, $message, $args );
75
+ }
76
 
77
+ /**
78
+ * Method: Log info message for sensor.
79
+ *
80
+ * @param string $message - Alert message.
81
+ * @param mix $args - Message arguments.
82
+ */
83
+ protected function LogInfo( $message, $args ) {
84
+ $this->Log( 0003, $message, $args );
85
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ /**
88
+ * Check to see whether or not the specified directory is accessible
89
+ *
90
+ * @param string $dir_path - Directory path.
91
+ * @return boolean
92
+ */
93
+ protected function CheckDirectory( $dir_path ) {
94
+ if ( ! is_dir( $dir_path ) ) {
95
+ return false;
96
+ }
97
+ if ( ! is_readable( $dir_path ) ) {
98
+ return false;
99
+ }
100
+ if ( ! is_writable( $dir_path ) ) {
101
+ return false;
102
+ }
103
+ return true;
104
+ }
105
  }
classes/AbstractView.php CHANGED
@@ -1,209 +1,246 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
- *
5
  * Abstract class used in all the views.
 
6
  * @see Views/*.php
 
7
  */
8
- abstract class WSAL_AbstractView
9
- {
10
- /**
11
- * @var WpSecurityAuditLog
12
- */
13
- protected $_plugin;
14
-
15
- protected $_wpversion;
16
-
17
- /**
18
- * Contains the result to a call to add_submenu_page().
19
- * @var string
20
- */
21
- public $hook_suffix = '';
22
-
23
- /**
24
- * Tells us whether this view is currently being displayed or not.
25
- * @var boolean
26
- */
27
- public $is_active = false;
28
-
29
- public static $AllowedNoticeNames = array();
30
-
31
- /**
32
- * @param WpSecurityAuditLog $plugin
33
- */
34
- public function __construct(WpSecurityAuditLog $plugin)
35
- {
36
- $this->_plugin = $plugin;
37
-
38
- // get and store wordpress version
39
- global $wp_version;
40
- if (!isset($wp_version)) {
41
- $wp_version = get_bloginfo('version');
42
- }
43
- $this->_wpversion = floatval($wp_version);
44
-
45
- // handle admin notices
46
- add_action('wp_ajax_AjaxDismissNotice', array($this, 'AjaxDismissNotice'));
47
- }
48
-
49
- /**
50
- * Dismiss an admin notice through ajax.
51
- * @internal
52
- */
53
- public function AjaxDismissNotice()
54
- {
55
- if (!$this->_plugin->settings->CurrentUserCan('view')) {
56
- die('Access Denied.');
57
- }
58
-
59
- if (!isset($_REQUEST['notice'])) {
60
- die('Notice name expected as "notice" parameter.');
61
- }
62
-
63
- $this->DismissNotice($_REQUEST['notice']);
64
- }
65
-
66
- /**
67
- * @param string $name Name of notice.
68
- * @return boolean Whether notice got dismissed or not.
69
- */
70
- public function IsNoticeDismissed($name)
71
- {
72
- $user_id = get_current_user_id();
73
- $meta_key = 'wsal-notice-' . $name;
74
- self::$AllowedNoticeNames[] = $name;
75
- return !!get_user_meta($user_id, $meta_key, true);
76
- }
77
-
78
- /**
79
- * @param string $name Name of notice to dismiss.
80
- */
81
- public function DismissNotice($name)
82
- {
83
- $user_id = get_current_user_id();
84
- $meta_key = 'wsal-notice-' . $name;
85
- $old_value = get_user_meta($user_id, $meta_key, true);
86
- if (in_array($name, self::$AllowedNoticeNames) || $old_value === false) {
87
- update_user_meta($user_id, $meta_key, '1');
88
- }
89
- }
90
-
91
- /**
92
- * @param string $name Makes this notice available.
93
- */
94
- public function RegisterNotice($name)
95
- {
96
- self::$AllowedNoticeNames[] = $name;
97
- }
98
-
99
- /**
100
- * @return string Return page name (for menu etc).
101
- */
102
- abstract public function GetName();
103
-
104
- /**
105
- * @return string Return page title.
106
- */
107
- abstract public function GetTitle();
108
-
109
- /**
110
- * @return string Page icon name.
111
- */
112
- abstract public function GetIcon();
113
-
114
- /**
115
- * @return int Menu weight, the higher this is, the lower it goes.
116
- */
117
- abstract public function GetWeight();
118
-
119
- /**
120
- * Renders and outputs the view directly.
121
- */
122
- abstract public function Render();
123
-
124
- /**
125
- * Renders the view icon (this has been deprecated in newwer WP versions).
126
- */
127
- public function RenderIcon()
128
- {
129
- ?><div id="icon-plugins" class="icon32"><br></div><?php
130
- }
131
-
132
- /**
133
- * Renders the view title.
134
- */
135
- public function RenderTitle()
136
- {
137
- ?><h2><?php echo esc_html($this->GetTitle()); ?></h2><?php
138
- }
139
-
140
- /**
141
- * @link self::Render()
142
- */
143
- public function RenderContent()
144
- {
145
- $this->Render();
146
- }
147
-
148
- /**
149
- * @return boolean Whether page should appear in menu or not.
150
- */
151
- public function IsVisible()
152
- {
153
- return true;
154
- }
155
-
156
- /**
157
- * @return boolean Whether page should be accessible or not.
158
- */
159
- public function IsAccessible()
160
- {
161
- return true;
162
- }
163
-
164
- /**
165
- * Used for rendering stuff into head tag.
166
- */
167
- public function Header()
168
- {}
169
-
170
- /**
171
- * Used for rendering stuff in page fotoer.
172
- */
173
- public function Footer()
174
- {}
175
-
176
- /**
177
- * @return string Safe view menu name.
178
- */
179
- public function GetSafeViewName()
180
- {
181
- return 'wsal-' . preg_replace('/[^A-Za-z0-9\-]/', '-', $this->GetViewName());
182
- }
183
-
184
- /**
185
- * Override this and make it return true to create a shortcut link in plugin page to the view.
186
- * @return boolean
187
- */
188
- public function HasPluginShortcutLink()
189
- {
190
- return false;
191
- }
192
-
193
- /**
194
- * @return string URL to backend page for displaying view.
195
- */
196
- public function GetUrl()
197
- {
198
- $fn = function_exists('network_admin_url') ? 'network_admin_url' : 'admin_url';
199
- return $fn('admin.php?page=' . $this->GetSafeViewName());
200
- }
201
-
202
- /**
203
- * @return string Generates view name out of class name.
204
- */
205
- public function GetViewName()
206
- {
207
- return strtolower(str_replace(array('WSAL_Views_', 'WSAL_'), '', get_class($this)));
208
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
1
  <?php
2
  /**
 
 
3
  * Abstract class used in all the views.
4
+ *
5
  * @see Views/*.php
6
+ * @package Wsal
7
  */
8
+ abstract class WSAL_AbstractView {
9
+
10
+ /**
11
+ * Instance of WpSecurityAuditLog.
12
+ *
13
+ * @var object
14
+ */
15
+ protected $_plugin;
16
+
17
+ /**
18
+ * WordPress version.
19
+ *
20
+ * @var string
21
+ */
22
+ protected $_wpversion;
23
+
24
+ /**
25
+ * Contains the result to a call to add_submenu_page().
26
+ *
27
+ * @var string
28
+ */
29
+ public $hook_suffix = '';
30
+
31
+ /**
32
+ * Tells us whether this view is currently being displayed or not.
33
+ *
34
+ * @var boolean
35
+ */
36
+ public $is_active = false;
37
+
38
+ /**
39
+ * Allowed notice names.
40
+ *
41
+ * @var array
42
+ */
43
+ public static $AllowedNoticeNames = array();
44
+
45
+ /**
46
+ * Method: Constructor.
47
+ *
48
+ * @param object $plugin - Instance of WpSecurityAuditLog.
49
+ */
50
+ public function __construct( WpSecurityAuditLog $plugin ) {
51
+ $this->_plugin = $plugin;
52
+
53
+ // Get and store WordPress version.
54
+ global $wp_version;
55
+ if ( ! isset( $wp_version ) ) {
56
+ $wp_version = get_bloginfo( 'version' );
57
+ }
58
+ $this->_wpversion = floatval( $wp_version );
59
+
60
+ // Handle admin notices.
61
+ add_action( 'wp_ajax_AjaxDismissNotice', array( $this, 'AjaxDismissNotice' ) );
62
+ }
63
+
64
+ /**
65
+ * Dismiss an admin notice through ajax.
66
+ *
67
+ * @internal
68
+ */
69
+ public function AjaxDismissNotice() {
70
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
71
+ die( 'Access Denied.' );
72
+ }
73
+
74
+ // Filter $_POST array for security.
75
+ $post_array = filter_input_array( INPUT_POST );
76
+
77
+ if ( ! isset( $post_array['notice'] ) ) {
78
+ die( 'Notice name expected as "notice" parameter.' );
79
+ }
80
+
81
+ $this->DismissNotice( $post_array['notice'] );
82
+ }
83
+
84
+ /**
85
+ * Method: Check if notice is dismissed.
86
+ *
87
+ * @param string $name — Name of notice.
88
+ * @return boolean — Whether notice got dismissed or not.
89
+ */
90
+ public function IsNoticeDismissed( $name ) {
91
+ $user_id = get_current_user_id();
92
+ $meta_key = 'wsal-notice-' . $name;
93
+ self::$AllowedNoticeNames[] = $name;
94
+ return ! ! get_user_meta( $user_id, $meta_key, true );
95
+ }
96
+
97
+ /**
98
+ * Method: Dismiss notice.
99
+ *
100
+ * @param string $name Name of notice to dismiss.
101
+ */
102
+ public function DismissNotice( $name ) {
103
+ $user_id = get_current_user_id();
104
+ $meta_key = 'wsal-notice-' . $name;
105
+ $old_value = get_user_meta( $user_id, $meta_key, true );
106
+ if ( in_array( $name, self::$AllowedNoticeNames ) || false === $old_value ) {
107
+ update_user_meta( $user_id, $meta_key, '1' );
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Method: Register notice.
113
+ *
114
+ * @param string $name — Makes this notice available.
115
+ */
116
+ public function RegisterNotice( $name ) {
117
+ self::$AllowedNoticeNames[] = $name;
118
+ }
119
+
120
+ /**
121
+ * Method: Return page name (for menu etc).
122
+ *
123
+ * @return string
124
+ */
125
+ abstract public function GetName();
126
+
127
+ /**
128
+ * Method: Return page title.
129
+ *
130
+ * @return string
131
+ */
132
+ abstract public function GetTitle();
133
+
134
+ /**
135
+ * Method: Page icon name.
136
+ *
137
+ * @return string
138
+ */
139
+ abstract public function GetIcon();
140
+
141
+ /**
142
+ * Method: Menu weight, the higher this is, the lower it goes.
143
+ *
144
+ * @return int
145
+ */
146
+ abstract public function GetWeight();
147
+
148
+ /**
149
+ * Renders and outputs the view directly.
150
+ */
151
+ abstract public function Render();
152
+
153
+ /**
154
+ * Renders the view icon (this has been deprecated in newwer WP versions).
155
+ */
156
+ public function RenderIcon() {
157
+ ?>
158
+ <div id="icon-plugins" class="icon32"><br></div>
159
+ <?php
160
+ }
161
+
162
+ /**
163
+ * Renders the view title.
164
+ */
165
+ public function RenderTitle() {
166
+ ?>
167
+ <h2><?php echo esc_html( $this->GetTitle() ); ?></h2>
168
+ <?php
169
+ }
170
+
171
+ /**
172
+ * Method: Render content of the view.
173
+ *
174
+ * @link self::Render()
175
+ */
176
+ public function RenderContent() {
177
+ $this->Render();
178
+ }
179
+
180
+ /**
181
+ * Method: Whether page should appear in menu or not.
182
+ *
183
+ * @return boolean
184
+ */
185
+ public function IsVisible() {
186
+ return true;
187
+ }
188
+
189
+ /**
190
+ * Method: Whether page should be accessible or not.
191
+ *
192
+ * @return boolean
193
+ */
194
+ public function IsAccessible() {
195
+ return true;
196
+ }
197
+
198
+ /**
199
+ * Used for rendering stuff into head tag.
200
+ */
201
+ public function Header() {
202
+ }
203
+
204
+ /**
205
+ * Used for rendering stuff in page fotoer.
206
+ */
207
+ public function Footer() {
208
+ }
209
+
210
+ /**
211
+ * Method: Safe view menu name.
212
+ *
213
+ * @return string
214
+ */
215
+ public function GetSafeViewName() {
216
+ return 'wsal-' . preg_replace( '/[^A-Za-z0-9\-]/', '-', $this->GetViewName() );
217
+ }
218
+
219
+ /**
220
+ * Override this and make it return true to create a shortcut link in plugin page to the view.
221
+ *
222
+ * @return boolean
223
+ */
224
+ public function HasPluginShortcutLink() {
225
+ return false;
226
+ }
227
+
228
+ /**
229
+ * Method: URL to backend page for displaying view.
230
+ *
231
+ * @return string
232
+ */
233
+ public function GetUrl() {
234
+ $fn = function_exists( 'network_admin_url' ) ? 'network_admin_url' : 'admin_url';
235
+ return $fn( 'admin.php?page=' . $this->GetSafeViewName() );
236
+ }
237
+
238
+ /**
239
+ * Method: Generates view name out of class name.
240
+ *
241
+ * @return string
242
+ */
243
+ public function GetViewName() {
244
+ return strtolower( str_replace( array( 'WSAL_Views_', 'WSAL_' ), '', get_class( $this ) ) );
245
+ }
246
  }
classes/Adapters/ActiveRecordInterface.php CHANGED
@@ -1,19 +1,91 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * Interface used by the ActiveRecord.
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  */
7
- interface WSAL_Adapters_ActiveRecordInterface
8
- {
9
- public function IsInstalled();
10
- public function Install();
11
- public function Uninstall();
12
- public function Load($cond = '%d', $args = array(1));
13
- public function Save($activeRecord);
14
- public function Delete($activeRecord);
15
- public function LoadMulti($cond, $args = array());
16
- public function LoadAndCallForEach($callback, $cond = '%d', $args = array(1));
17
- public function Count($cond = '%d', $args = array(1));
18
- public function LoadMultiQuery($query, $args = array());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
1
  <?php
2
  /**
3
+ * Active Record Interface.
4
  *
5
  * Interface used by the ActiveRecord.
6
+ *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Interface used by the ActiveRecord.
17
+ *
18
+ * @package Wsal
19
  */
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
+
37
+ /**
38
+ * Load.
39
+ *
40
+ * @param string $cond - Query Condition.
41
+ * @param array $args - Query arguments.
42
+ */
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
  }
classes/Adapters/MetaInterface.php CHANGED
@@ -1,17 +1,37 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
5
  * Interface used by the Metadata.
 
 
6
  */
7
- interface WSAL_Adapters_MetaInterface
8
- {
9
- /**
10
- * Create a meta object
11
- * @param $metaData Array of meta data
12
- * @return int ID of the new meta data
13
- */
14
- public function deleteByOccurenceIds($occurenceIds);
 
15
 
16
- public function loadByNameAndOccurenceId($metaName, $occurenceId);
 
 
 
 
 
 
17
  }
1
  <?php
2
  /**
3
+ * Meta Interface.
4
+ *
5
+ * Interface used by the Metadata.
6
  *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * Interface used by the Metadata.
17
+ *
18
+ * @package Wsal
19
  */
20
+ interface WSAL_Adapters_MetaInterface {
21
+
22
+ /**
23
+ * Create a meta object
24
+ *
25
+ * @param array $occurenceIds - Array of meta data.
26
+ * @return int ID of the new meta data
27
+ */
28
+ public function deleteByOccurenceIds( $occurenceIds );
29
 
30
+ /**
31
+ * Load by name and occurrence id.
32
+ *
33
+ * @param string $metaName - Meta name.
34
+ * @param int $occurenceId - Occurrence ID.
35
+ */
36
+ public function loadByNameAndOccurenceId( $metaName, $occurenceId );
37
  }
classes/Adapters/MySQL/ActiveRecordAdapter.php CHANGED
@@ -1,679 +1,711 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * MySQL database ActiveRecord class.
5
  *
6
  * MySQL generic table used for Save, Read, Create or Delete
7
  * elements in the Database.
8
  * There are also the functions used in the Report Add-On to get the reports.
 
 
9
  */
10
- class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInterface
11
- {
12
- protected $connection;
13
-
14
- /**
15
- * Contains the table name
16
- * @var string
17
- */
18
- protected $_table;
19
-
20
- /**
21
- * Contains primary key column name, override as required.
22
- * @var string
23
- */
24
- protected $_idkey = '';
25
-
26
- public function __construct($conn)
27
- {
28
- $this->connection = $conn;
29
- }
30
-
31
- /**
32
- * @return WSAL_Models_ActiveRecord
33
- */
34
- public function GetModel()
35
- {
36
- return new WSAL_Models_ActiveRecord();
37
- }
38
-
39
- /**
40
- * @return string Returns table name.
41
- */
42
- public function GetTable()
43
- {
44
- $_wpdb = $this->connection;
45
- return $_wpdb->base_prefix . $this->_table;
46
- }
47
-
48
- /**
49
- * Used for WordPress prefix
50
- * @return string Returns table name of WordPress.
51
- */
52
- public function GetWPTable()
53
- {
54
- global $wpdb;
55
- return $wpdb->base_prefix . $this->_table;
56
- }
57
-
58
- /**
59
- * @return string SQL table options (constraints, foreign keys, indexes etc).
60
- */
61
- protected function GetTableOptions()
62
- {
63
- return ' PRIMARY KEY (' . $this->_idkey . ')';
64
- }
65
-
66
- /**
67
- * @return array Returns this records' columns.
68
- */
69
- public function GetColumns()
70
- {
71
- $model = $this->GetModel();
72
-
73
- if (!isset($this->_column_cache)) {
74
- $this->_column_cache = array();
75
- foreach (array_keys(get_object_vars($model)) as $col) {
76
- if (trim($col) && $col[0] != '_') {
77
- $this->_column_cache[] = $col;
78
- }
79
- }
80
- }
81
- return $this->_column_cache;
82
- }
83
-
84
- /**
85
- * @deprecated
86
- * @return boolean Returns whether table structure is installed or not.
87
- */
88
- public function IsInstalled()
89
- {
90
- $_wpdb = $this->connection;
91
- $sql = 'SHOW TABLES LIKE "' . $this->GetTable() . '"';
92
- return strtolower($_wpdb->get_var($sql)) == strtolower($this->GetTable());
93
- }
94
-
95
- /**
96
- * Install this ActiveRecord structure into DB.
97
- */
98
- public function Install()
99
- {
100
- $_wpdb = $this->connection;
101
- $_wpdb->query($this->_GetInstallQuery());
102
- }
103
-
104
- /**
105
- * Install this ActiveRecord structure into DB WordPress.
106
- */
107
- public function InstallOriginal()
108
- {
109
- global $wpdb;
110
- $wpdb->query($this->_GetInstallQuery(true));
111
- }
112
-
113
- /**
114
- * Remove this ActiveRecord structure from DB.
115
- */
116
- public function Uninstall()
117
- {
118
- $_wpdb = $this->connection;
119
- $_wpdb->query($this->_GetUninstallQuery());
120
- }
121
-
122
- /**
123
- * Save an active record into DB.
124
- * @return integer|boolean Either the number of modified/inserted rows or false on failure.
125
- */
126
- public function Save($activeRecord)
127
- {
128
- $_wpdb = $this->connection;
129
- $copy = $activeRecord;
130
- $data = array();
131
- $format = array();
132
-
133
- foreach ($this->GetColumns() as $index => $key) {
134
- if ($key == $this->_idkey) {
135
- $_idIndex = $index;
136
- }
137
-
138
- $val = $copy->$key;
139
- $deffmt = '%s';
140
- if (is_int($copy->$key)) {
141
- $deffmt = '%d';
142
- }
143
- if (is_float($copy->$key)) {
144
- $deffmt = '%f';
145
- }
146
- if (is_array($copy->$key) || is_object($copy->$key)) {
147
- $data[$key] = WSAL_Helpers_DataHelper::JsonEncode($val);
148
- } else {
149
- $data[$key] = $val;
150
- }
151
- $format[] = $deffmt;
152
- }
153
-
154
- if (isset($data[$this->_idkey]) && empty($data[$this->_idkey])) {
155
- unset($data[$this->_idkey]);
156
- unset($format[$_idIndex]);
157
- }
158
-
159
- $result = $_wpdb->replace($this->GetTable(), $data, $format);
160
-
161
- if ($result !== false) {
162
- if ($_wpdb->insert_id) {
163
- $copy->setId($_wpdb->insert_id);
164
- }
165
- }
166
- return $result;
167
- }
168
-
169
- /**
170
- * Load record from DB (Single row).
171
- * @param string $cond (Optional) Load condition.
172
- * @param array $args (Optional) Load condition arguments.
173
- */
174
- public function Load($cond = '%d', $args = array(1))
175
- {
176
- $_wpdb = $this->connection;
177
-
178
- $sql = $_wpdb->prepare('SELECT * FROM '.$this->GetTable().' WHERE '. $cond, $args);
179
- $data = $_wpdb->get_row($sql, ARRAY_A);
180
-
181
- return $data;
182
- }
183
-
184
- /**
185
- * Load records from DB (Multi rows).
186
- * @param string $cond Load condition.
187
- * @param array $args (Optional) Load condition arguments.
188
- */
189
- public function LoadArray($cond, $args = array())
190
- {
191
- $_wpdb = $this->connection;
192
- $result = array();
193
- $sql = $_wpdb->prepare('SELECT * FROM '.$this->GetTable().' WHERE '. $cond, $args);
194
- foreach ($_wpdb->get_results($sql, ARRAY_A) as $data) {
195
- $result[] = $this->getModel()->LoadData($data);
196
- }
197
- return $result;
198
- }
199
-
200
- /**
201
- * Delete DB record.
202
- * @return int|boolean Either the amount of deleted rows or False on error.
203
- */
204
- public function Delete($activeRecord)
205
- {
206
- $_wpdb = $this->connection;
207
- $result = $_wpdb->delete(
208
- $this->GetTable(),
209
- $activeRecord->getId()
210
- );
211
- return $result;
212
- }
213
-
214
- /**
215
- * Delete records in DB matching a query.
216
- * @param string $query Full SQL query.
217
- * @param array $args (Optional) Query arguments.
218
- */
219
- public function DeleteQuery($query, $args = array())
220
- {
221
- $_wpdb = $this->connection;
222
- $sql = count($args) ? $_wpdb->prepare($query, $args) : $query;
223
- $result = $_wpdb->query($sql);
224
- return $result;
225
- }
226
-
227
- /**
228
- * Load multiple records from DB.
229
- * @param string $cond (Optional) Load condition (eg: 'some_id = %d' ).
230
- * @param array $args (Optional) Load condition arguments (rg: array(45) ).
231
- * @return self[] List of loaded records.
232
- */
233
- public function LoadMulti($cond, $args = array())
234
- {
235
- $_wpdb = $this->connection;
236
- $result = array();
237
- $sql = (!is_array($args) || !count($args)) // do we really need to prepare() or not?
238
- ? ($cond)
239
- : $_wpdb->prepare($cond, $args)
240
- ;
241
- foreach ($_wpdb->get_results($sql, ARRAY_A) as $data) {
242
- $result[] = $this->getModel()->LoadData($data);
243
- }
244
- return $result;
245
- }
246
-
247
- /**
248
- * Load multiple records from DB and call a callback for each record.
249
- * This function is very memory-efficient, it doesn't load records in bulk.
250
- * @param callable $callback The callback to invoke.
251
- * @param string $cond (Optional) Load condition.
252
- * @param array $args (Optional) Load condition arguments.
253
- */
254
- public function LoadAndCallForEach($callback, $cond = '%d', $args = array(1))
255
- {
256
- $_wpdb = $this->connection;
257
- $class = get_called_class();
258
- $sql = $_wpdb->prepare('SELECT * FROM ' . $this->GetTable() . ' WHERE '.$cond, $args);
259
- foreach ($_wpdb->get_results($sql, ARRAY_A) as $data) {
260
- call_user_func($callback, new $class($data));
261
- }
262
- }
263
-
264
- /**
265
- * Count records in the DB matching a condition.
266
- * If no parameters are given, this counts the number of records in the DB table.
267
- * @param string $cond (Optional) Query condition.
268
- * @param array $args (Optional) Condition arguments.
269
- * @return int Number of matching records.
270
- */
271
- public function Count($cond = '%d', $args = array(1))
272
- {
273
- $_wpdb = $this->connection;
274
- $class = get_called_class();
275
- $sql = $_wpdb->prepare('SELECT COUNT(*) FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args);
276
- return (int)$_wpdb->get_var($sql);
277
- }
278
-
279
- /**
280
- * Count records in the DB matching a query.
281
- * @param string $query Full SQL query.
282
- * @param array $args (Optional) Query arguments.
283
- * @return int Number of matching records.
284
- */
285
- public function CountQuery($query, $args = array())
286
- {
287
- $_wpdb = $this->connection;
288
- $sql = count($args) ? $_wpdb->prepare($query, $args) : $query;
289
- return (int)$_wpdb->get_var($sql);
290
- }
291
-
292
- /**
293
- * Similar to LoadMulti but allows the use of a full SQL query.
294
- * @param string $query Full SQL query.
295
- * @param array $args (Optional) Query arguments.
296
- * @return self[] List of loaded records.
297
- */
298
- public function LoadMultiQuery($query, $args = array())
299
- {
300
- $_wpdb = $this->connection;
301
- $class = get_called_class();
302
- $result = array();
303
- $sql = count($args) ? $_wpdb->prepare($query, $args) : $query;
304
- foreach ($_wpdb->get_results($sql, ARRAY_A) as $data) {
305
- $result[] = $this->getModel()->LoadData($data);
306
- }
307
- return $result;
308
- }
309
-
310
- /**
311
- * @param string $prefix (Optional) table prefix
312
- * @return string Must return SQL for creating table.
313
- */
314
- protected function _GetInstallQuery($prefix = false)
315
- {
316
- $_wpdb = $this->connection;
317
-
318
- $class = get_class($this);
319
- $copy = new $class($this->connection);
320
- $table_name = ($prefix) ? $this->GetWPTable() : $this->GetTable();
321
- $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
322
-
323
- foreach ($this->GetColumns() as $key) {
324
- $sql .= ' ';
325
- switch (true) {
326
- case $key == $copy->_idkey:
327
- $sql .= $key . ' BIGINT NOT NULL AUTO_INCREMENT,' . PHP_EOL;
328
- break;
329
- case is_integer($copy->$key):
330
- $sql .= $key . ' BIGINT NOT NULL,' . PHP_EOL;
331
- break;
332
- case is_float($copy->$key):
333
- $sql .= $key . ' DOUBLE NOT NULL,' . PHP_EOL;
334
- break;
335
- case is_string($copy->$key):
336
- $maxlength = $key . '_maxlength';
337
- if (property_exists($class, $maxlength)) {
338
- $sql .= $key . ' VARCHAR(' . intval($class::$$maxlength) . ') NOT NULL,' . PHP_EOL;
339
- } else {
340
- $sql .= $key . ' TEXT NOT NULL,' . PHP_EOL;
341
- }
342
- break;
343
- case is_bool($copy->$key):
344
- $sql .= $key . ' BIT NOT NULL,' . PHP_EOL;
345
- break;
346
- case is_array($copy->$key):
347
- case is_object($copy->$key):
348
- $sql .= $key . ' LONGTEXT NOT NULL,' . PHP_EOL;
349
- break;
350
- }
351
- }
352
-
353
- $sql .= $this->GetTableOptions() . PHP_EOL;
354
-
355
- $sql .= ')';
356
-
357
- if (!empty($_wpdb->charset)) {
358
- $sql .= ' DEFAULT CHARACTER SET ' . $_wpdb->charset;
359
- }
360
-
361
- return $sql;
362
- }
363
-
364
- /**
365
- * @return string Must return SQL for removing table (at a minimum, it should be ` 'DROP TABLE ' . $this->_table `).
366
- */
367
- protected function _GetUninstallQuery()
368
- {
369
- return 'DROP TABLE ' . $this->GetTable();
370
- }
371
-
372
- /**
373
- * Get Users user_login.
374
- * @param int $_userId user ID
375
- * @return string comma separated users login
376
- */
377
- private function GetUserNames($_userId)
378
- {
379
- global $wpdb;
380
-
381
- $user_names = '0';
382
- if (!empty($_userId) && $_userId != "null") {
383
- $sql = 'SELECT user_login FROM '. $wpdb->users .' WHERE find_in_set(ID, @userId) > 0';
384
- $wpdb->query("SET @userId = $_userId");
385
- $result = $wpdb->get_results($sql, ARRAY_A);
386
- $aUsers = array();
387
- foreach ($result as $item) {
388
- $aUsers[] = '"'.$item['user_login'].'"';
389
- }
390
- $user_names = implode(', ', $aUsers);
391
- }
392
- return $user_names;
393
- }
394
-
395
- /**
396
- * Function used in WSAL reporting extension.
397
- * @param int $_siteId site ID
398
- * @param int $_userId user ID
399
- * @param string $_roleName user role
400
- * @param int $_alertCode alert code
401
- * @param timestamp $_startTimestamp from created_on
402
- * @param timestamp $_endTimestamp to created_on
403
- * @param timestamp $_nextDate (Optional) created_on >
404
- * @param int $_limit (Optional) limit
405
- * @return array Report results
406
- */
407
- public function GetReporting($_siteId, $_userId, $_roleName, $_alertCode, $_startTimestamp, $_endTimestamp, $_nextDate = null, $_limit = 0)
408
- {
409
- global $wpdb;
410
- $user_names = $this->GetUserNames($_userId);
411
-
412
- $_wpdb = $this->connection;
413
- $_wpdb->set_charset($_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci');
414
- // tables
415
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
416
- $tableMeta = $meta->GetTable(); // metadata
417
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($this->connection);
418
- $tableOcc = $occurrence->GetTable(); // occurrences
419
-
420
- $conditionDate = !empty($_nextDate) ? ' AND occ.created_on < '.$_nextDate : '';
421
-
422
- $sql = "SELECT DISTINCT
423
- occ.id,
424
- occ.alert_id,
425
- occ.site_id,
426
- occ.created_on,
427
- replace(replace(replace((
428
- SELECT t1.value FROM $tableMeta AS t1 WHERE t1.name = 'CurrentUserRoles' AND t1.occurrence_id = occ.id LIMIT 1), '[', ''), ']', ''), '\\'', '') AS roles,
429
- (SELECT replace(t2.value, '\"','') FROM $tableMeta as t2 WHERE t2.name = 'ClientIP' AND t2.occurrence_id = occ.id LIMIT 1) AS ip,
430
- (SELECT replace(t3.value, '\"', '') FROM $tableMeta as t3 WHERE t3.name = 'UserAgent' AND t3.occurrence_id = occ.id LIMIT 1) AS ua,
431
- COALESCE(
432
- (SELECT replace(t4.value, '\"', '') FROM $tableMeta as t4 WHERE t4.name = 'Username' AND t4.occurrence_id = occ.id LIMIT 1),
433
- (SELECT replace(t5.value, '\"', '') FROM $tableMeta as t5 WHERE t5.name = 'CurrentUserID' AND t5.occurrence_id = occ.id LIMIT 1)
434
- ) as user_id
435
- FROM $tableOcc AS occ
436
- JOIN $tableMeta AS meta ON meta.occurrence_id = occ.id
437
- WHERE
438
- (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
439
- AND (@userId is NULL OR (
440
- (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
441
- OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
442
- ))
443
- AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
444
- AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
445
- ))
446
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
447
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
448
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
449
- {$conditionDate}
450
- ORDER BY
451
- created_on DESC
452
- ";
453
-
454
- $_wpdb->query("SET @siteId = $_siteId");
455
- $_wpdb->query("SET @userId = $_userId");
456
- $_wpdb->query("SET @roleName = $_roleName");
457
- $_wpdb->query("SET @alertCode = $_alertCode");
458
- $_wpdb->query("SET @startTimestamp = $_startTimestamp");
459
- $_wpdb->query("SET @endTimestamp = $_endTimestamp");
460
-
461
- if (!empty($_limit)) {
462
- $sql .= " LIMIT {$_limit}";
463
- }
464
- $results = $_wpdb->get_results($sql);
465
- if (!empty($results)) {
466
- foreach ($results as $row) {
467
- $sql = "SELECT t6.ID FROM $wpdb->users AS t6 WHERE t6.user_login = \"$row->user_id\"";
468
- $userId = $wpdb->get_var($sql);
469
- if ($userId == null) {
470
- $sql = "SELECT t4.ID FROM $wpdb->users AS t4 WHERE t4.ID = \"$row->user_id\"";
471
- $userId = $wpdb->get_var($sql);
472
- }
473
- $row->user_id = $userId;
474
- $results['lastDate'] = $row->created_on;
475
- }
476
- }
477
-
478
- return $results;
479
- }
480
-
481
- /**
482
- * Function used in WSAL reporting extension.
483
- * Check if criteria are matching in the DB.
484
- * @param mixed $criteria query conditions
485
- * @return int count of distinct values
486
- */
487
- public function CheckMatchReportCriteria($criteria)
488
- {
489
- $_siteId = $criteria['siteId'];
490
- $_userId = $criteria['userId'];
491
- $_roleName = $criteria['roleName'];
492
- $_alertCode = $criteria['alertCode'];
493
- $_startTimestamp = $criteria['startTimestamp'];
494
- $_endTimestamp = $criteria['endTimestamp'];
495
- $_ipAddress = $criteria['ipAddress'];
496
-
497
- $_wpdb = $this->connection;
498
- $_wpdb->set_charset($_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci');
499
- // tables
500
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
501
- $tableMeta = $meta->GetTable(); // metadata
502
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($this->connection);
503
- $tableOcc = $occurrence->GetTable(); // occurrences
504
-
505
- $user_names = $this->GetUserNames($_userId);
506
-
507
- $sql = "SELECT COUNT(DISTINCT occ.id) FROM $tableOcc AS occ
508
- JOIN $tableMeta AS meta ON meta.occurrence_id = occ.id
509
- WHERE
510
- (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
511
- AND (@userId is NULL OR (
512
- (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
513
- OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
514
- ))
515
- AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
516
- AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
517
- ))
518
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
519
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
520
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
521
- AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
522
- ";
523
-
524
- $_wpdb->query("SET @siteId = $_siteId");
525
- $_wpdb->query("SET @userId = $_userId");
526
- $_wpdb->query("SET @roleName = $_roleName");
527
- $_wpdb->query("SET @alertCode = $_alertCode");
528
- $_wpdb->query("SET @startTimestamp = $_startTimestamp");
529
- $_wpdb->query("SET @endTimestamp = $_endTimestamp");
530
- $_wpdb->query("SET @ipAddress = $_ipAddress");
531
-
532
- $count = (int)$_wpdb->get_var($sql);
533
- return $count;
534
- }
535
-
536
- /**
537
- * Function used in WSAL reporting extension.
538
- * List of unique IP addresses used by the same user.
539
- * @param int $_siteId site ID
540
- * @param timestamp $_startTimestamp from created_on
541
- * @param timestamp $_endTimestamp to created_on
542
- * @param int $_userId (Optional) user ID
543
- * @param string $_roleName (Optional) user role
544
- * @param string $_ipAddress (Optional) IP address
545
- * @param int $_alertCode (Optional) alert code
546
- * @param int $_limit (Optional) limit
547
- * @return array Report results grouped by IP and Username
548
- */
549
- public function GetReportGrouped($_siteId, $_startTimestamp, $_endTimestamp, $_userId = 'null', $_roleName = 'null', $_ipAddress = 'null', $_alertCode = 'null', $_limit = 0)
550
- {
551
- global $wpdb;
552
- $user_names = $this->GetUserNames($_userId);
553
-
554
- $_wpdb = $this->connection;
555
- $_wpdb->set_charset($_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci');
556
- // tables
557
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
558
- $tableMeta = $meta->GetTable(); // metadata
559
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($this->connection);
560
- $tableOcc = $occurrence->GetTable(); // occurrences
561
- // Get temp table `wsal_tmp_users`
562
- $tmp_users = new WSAL_Adapters_MySQL_TmpUser($this->connection);
563
- // if the table exist
564
- if ($tmp_users->IsInstalled()) {
565
- $tableUsers = $tmp_users->GetTable(); // tmp_users
566
- $this->TempUsers($tableUsers);
567
- } else {
568
- $tableUsers = $wpdb->users;
569
- }
570
-
571
- $sql = "SELECT DISTINCT *
572
- FROM (SELECT DISTINCT
573
- occ.site_id,
574
- CONVERT((SELECT replace(t1.value, '\"', '') FROM $tableMeta as t1 WHERE t1.name = 'Username' AND t1.occurrence_id = occ.id LIMIT 1) using UTF8) AS user_login ,
575
- CONVERT((SELECT replace(t3.value, '\"','') FROM $tableMeta as t3 WHERE t3.name = 'ClientIP' AND t3.occurrence_id = occ.id LIMIT 1) using UTF8) AS ip
576
- FROM $tableOcc AS occ
577
- JOIN $tableMeta AS meta ON meta.occurrence_id = occ.id
578
- WHERE
579
- (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
580
- AND (@userId is NULL OR (
581
- (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
582
- OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
583
- ))
584
- AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
585
- AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
586
- ))
587
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
588
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
589
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
590
- AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
591
- HAVING user_login IS NOT NULL
592
- UNION ALL
593
- SELECT DISTINCT
594
- occ.site_id,
595
- CONVERT((SELECT u.user_login
596
- FROM $tableMeta as t2
597
- JOIN $tableUsers AS u ON u.ID = replace(t2.value, '\"', '')
598
- WHERE t2.name = 'CurrentUserID'
599
- AND t2.occurrence_id = occ.id
600
- GROUP BY u.ID
601
- LIMIT 1) using UTF8) AS user_login,
602
- CONVERT((SELECT replace(t4.value, '\"','') FROM $tableMeta as t4 WHERE t4.name = 'ClientIP' AND t4.occurrence_id = occ.id LIMIT 1) using UTF8) AS ip
603
- FROM $tableOcc AS occ
604
- JOIN $tableMeta AS meta ON meta.occurrence_id = occ.id
605
- WHERE
606
- (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
607
- AND (@userId is NULL OR (
608
- (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
609
- OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
610
- ))
611
- AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
612
- AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
613
- ))
614
- AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
615
- AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
616
- AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
617
- AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
618
- HAVING user_login IS NOT NULL) ip_logins
619
- WHERE user_login NOT IN ('Website Visitor', 'Plugins', 'Plugin')
620
- ORDER BY user_login ASC
621
- ";
622
- $_wpdb->query("SET @siteId = $_siteId");
623
- $_wpdb->query("SET @userId = $_userId");
624
- $_wpdb->query("SET @roleName = $_roleName");
625
- $_wpdb->query("SET @alertCode = $_alertCode");
626
- $_wpdb->query("SET @startTimestamp = $_startTimestamp");
627
- $_wpdb->query("SET @endTimestamp = $_endTimestamp");
628
- $_wpdb->query("SET @ipAddress = $_ipAddress");
629
- if (!empty($_limit)) {
630
- $sql .= " LIMIT {$_limit}";
631
- }
632
-
633
- $grouped_types = array();
634
- $results = $_wpdb->get_results($sql);
635
- if (!empty($results)) {
636
- foreach ($results as $key => $row) {
637
- // get the display_name only for the first row & if the user_login changed from the previous row
638
- if ($key == 0 || ($key > 1 && $results[($key - 1)]->user_login != $row->user_login)) {
639
- $sql = "SELECT t5.display_name FROM $wpdb->users AS t5 WHERE t5.user_login = \"$row->user_login\"";
640
- $displayName = $wpdb->get_var($sql);
641
- }
642
- $row->display_name = $displayName;
643
-
644
- if (!isset($grouped_types[$row->user_login])) {
645
- $grouped_types[$row->user_login] = array(
646
- 'site_id' => $row->site_id,
647
- 'user_login' => $row->user_login,
648
- 'display_name' => $row->display_name,
649
- 'ips' => array()
650
- );
651
- }
652
-
653
- $grouped_types[$row->user_login]['ips'][] = $row->ip;
654
- }
655
- }
656
-
657
- return $grouped_types;
658
- }
659
-
660
- /**
661
- * DELETE from table `tmp_users` and populate with users.
662
- * It is used in the query of the above function.
663
- * @param string $tableUsers table name
664
- */
665
- private function TempUsers($tableUsers)
666
- {
667
- $_wpdb = $this->connection;
668
- $sql = "DELETE FROM $tableUsers";
669
- $_wpdb->query($sql);
670
-
671
- $sql = "INSERT INTO $tableUsers (ID, user_login) VALUES " ;
672
- $users = get_users(array('fields' => array('ID', 'user_login')));
673
- foreach ($users as $user) {
674
- $sql .= '('. $user->ID .', \''. $user->user_login .'\'), ';
675
- }
676
- $sql = rtrim($sql, ", ");
677
- $_wpdb->query($sql);
678
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
679
  }
1
  <?php
2
  /**
3
+ * Adapter: Active Record.
4
+ *
5
+ * MySQL database ActiveRecord class.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * MySQL database ActiveRecord class.
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
23
  */
24
+ class WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_ActiveRecordInterface {
25
+
26
+ /**
27
+ * DB Connection
28
+ *
29
+ * @var array
30
+ */
31
+ protected $connection;
32
+
33
+ /**
34
+ * Contains the table name
35
+ *
36
+ * @var string
37
+ */
38
+ protected $_table;
39
+
40
+ /**
41
+ * Contains primary key column name, override as required.
42
+ *
43
+ * @var string
44
+ */
45
+ protected $_idkey = '';
46
+
47
+ /**
48
+ * Method: Constructor.
49
+ *
50
+ * @param array $conn - Connection array.
51
+ */
52
+ public function __construct( $conn ) {
53
+ $this->connection = $conn;
54
+ }
55
+
56
+ /**
57
+ * Returns the model class for adapter.
58
+ *
59
+ * @return WSAL_Models_ActiveRecord
60
+ */
61
+ public function GetModel() {
62
+ return new WSAL_Models_ActiveRecord();
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
+ /**
76
+ * Used for WordPress prefix
77
+ *
78
+ * @return string Returns table name of WordPress.
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
+ /**
95
+ * Returns this records' columns.
96
+ *
97
+ * @return array
98
+ */
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] != '_' ) {
106
+ $this->_column_cache[] = $col;
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
+ return strtolower( $_wpdb->get_var( $sql ) ) == strtolower( $this->GetTable() );
123
+ }
124
+
125
+ /**
126
+ * Install this ActiveRecord structure into DB.
127
+ */
128
+ public function Install() {
129
+ $_wpdb = $this->connection;
130
+ $_wpdb->query( $this->_GetInstallQuery() );
131
+ }
132
+
133
+ /**
134
+ * Install this ActiveRecord structure into DB WordPress.
135
+ */
136
+ public function InstallOriginal() {
137
+ global $wpdb;
138
+ $wpdb->query( $this->_GetInstallQuery( true ) );
139
+ }
140
+
141
+ /**
142
+ * Remove this ActiveRecord structure from DB.
143
+ */
144
+ public function Uninstall() {
145
+ $_wpdb = $this->connection;
146
+ $_wpdb->query( $this->_GetUninstallQuery() );
147
+ }
148
+
149
+ /**
150
+ * Save an active record into DB.
151
+ *
152
+ * @param object $active_record - ActiveRecord object.
153
+ * @return integer|boolean - Either the number of modified/inserted rows or false on failure.
154
+ */
155
+ public function Save( $active_record ) {
156
+ $_wpdb = $this->connection;
157
+ $copy = $active_record;
158
+ $data = array();
159
+ $format = array();
160
+
161
+ foreach ( $this->GetColumns() as $index => $key ) {
162
+ if ( $key == $this->_idkey ) {
163
+ $_id_index = $index;
164
+ }
165
+
166
+ $val = $copy->$key;
167
+ $deffmt = '%s';
168
+ if ( is_int( $copy->$key ) ) {
169
+ $deffmt = '%d';
170
+ }
171
+ if ( is_float( $copy->$key ) ) {
172
+ $deffmt = '%f';
173
+ }
174
+ if ( is_array( $copy->$key ) || is_object( $copy->$key ) ) {
175
+ $data[ $key ] = WSAL_Helpers_DataHelper::JsonEncode( $val );
176
+ } else {
177
+ $data[ $key ] = $val;
178
+ }
179
+ $format[] = $deffmt;
180
+ }
181
+
182
+ if ( isset( $data[ $this->_idkey ] ) && empty( $data[ $this->_idkey ] ) ) {
183
+ unset( $data[ $this->_idkey ] );
184
+ unset( $format[ $_id_index ] );
185
+ }
186
+
187
+ $result = $_wpdb->replace( $this->GetTable(), $data, $format );
188
+
189
+ if ( false !== $result ) {
190
+ if ( $_wpdb->insert_id ) {
191
+ $copy->setId( $_wpdb->insert_id );
192
+ }
193
+ }
194
+ return $result;
195
+ }
196
+
197
+ /**
198
+ * Load record from DB (Single row).
199
+ *
200
+ * @param string $cond - (Optional) Load condition.
201
+ * @param array $args - (Optional) Load condition arguments.
202
+ */
203
+ public function Load( $cond = '%d', $args = array( 1 ) ) {
204
+ $_wpdb = $this->connection;
205
+ $sql = $_wpdb->prepare( 'SELECT * FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
206
+ $data = $_wpdb->get_row( $sql, ARRAY_A );
207
+ return $data;
208
+ }
209
+
210
+ /**
211
+ * Load records from DB (Multi rows).
212
+ *
213
+ * @param string $cond Load condition.
214
+ * @param array $args (Optional) Load condition arguments.
215
+ */
216
+ public function LoadArray( $cond, $args = array() ) {
217
+ $_wpdb = $this->connection;
218
+ $result = array();
219
+ $sql = $_wpdb->prepare( 'SELECT * FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
220
+ foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
221
+ $result[] = $this->getModel()->LoadData( $data );
222
+ }
223
+ return $result;
224
+ }
225
+
226
+ /**
227
+ * Delete DB record.
228
+ *
229
+ * @param object $active_record - ActiveRecord object.
230
+ * @return int|boolean - Either the amount of deleted rows or False on error.
231
+ */
232
+ public function Delete( $active_record ) {
233
+ $_wpdb = $this->connection;
234
+ $result = $_wpdb->delete(
235
+ $this->GetTable(),
236
+ $active_record->getId()
237
+ );
238
+ return $result;
239
+ }
240
+
241
+ /**
242
+ * Delete records in DB matching a query.
243
+ *
244
+ * @param string $query Full SQL query.
245
+ * @param array $args (Optional) Query arguments.
246
+ */
247
+ public function DeleteQuery( $query, $args = array() ) {
248
+ $_wpdb = $this->connection;
249
+ $sql = count( $args ) ? $_wpdb->prepare( $query, $args ) : $query;
250
+ $result = $_wpdb->query( $sql );
251
+ return $result;
252
+ }
253
+
254
+ /**
255
+ * Load multiple records from DB.
256
+ *
257
+ * @param string $cond (Optional) Load condition (eg: 'some_id = %d' ).
258
+ * @param array $args (Optional) Load condition arguments (rg: array(45) ).
259
+ * @return self[] List of loaded records.
260
+ */
261
+ public function LoadMulti( $cond, $args = array() ) {
262
+ $_wpdb = $this->connection;
263
+ $result = array();
264
+ $sql = ( ! is_array( $args ) || ! count( $args )) // Do we really need to prepare() or not?
265
+ ? ($cond)
266
+ : $_wpdb->prepare( $cond, $args );
267
+ foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
268
+ $result[] = $this->getModel()->LoadData( $data );
269
+ }
270
+ return $result;
271
+ }
272
+
273
+ /**
274
+ * Load multiple records from DB and call a callback for each record.
275
+ * This function is very memory-efficient, it doesn't load records in bulk.
276
+ *
277
+ * @param callable $callback The callback to invoke.
278
+ * @param string $cond (Optional) Load condition.
279
+ * @param array $args (Optional) Load condition arguments.
280
+ */
281
+ public function LoadAndCallForEach( $callback, $cond = '%d', $args = array( 1 ) ) {
282
+ $_wpdb = $this->connection;
283
+ $class = get_called_class();
284
+ $sql = $_wpdb->prepare( 'SELECT * FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
285
+ foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
286
+ call_user_func( $callback, new $class( $data ) );
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Count records in the DB matching a condition.
292
+ * If no parameters are given, this counts the number of records in the DB table.
293
+ *
294
+ * @param string $cond (Optional) Query condition.
295
+ * @param array $args (Optional) Condition arguments.
296
+ * @return int Number of matching records.
297
+ */
298
+ public function Count( $cond = '%d', $args = array( 1 ) ) {
299
+ $_wpdb = $this->connection;
300
+ $class = get_called_class();
301
+ $sql = $_wpdb->prepare( 'SELECT COUNT(*) FROM ' . $this->GetTable() . ' WHERE ' . $cond, $args );
302
+ return (int) $_wpdb->get_var( $sql );
303
+ }
304
+
305
+ /**
306
+ * Count records in the DB matching a query.
307
+ *
308
+ * @param string $query Full SQL query.
309
+ * @param array $args (Optional) Query arguments.
310
+ * @return int Number of matching records.
311
+ */
312
+ public function CountQuery( $query, $args = array() ) {
313
+ $_wpdb = $this->connection;
314
+ $sql = count( $args ) ? $_wpdb->prepare( $query, $args ) : $query;
315
+ return (int) $_wpdb->get_var( $sql );
316
+ }
317
+
318
+ /**
319
+ * Similar to LoadMulti but allows the use of a full SQL query.
320
+ *
321
+ * @param string $query Full SQL query.
322
+ * @param array $args (Optional) Query arguments.
323
+ * @return self[] List of loaded records.
324
+ */
325
+ public function LoadMultiQuery( $query, $args = array() ) {
326
+ $_wpdb = $this->connection;
327
+ $class = get_called_class();
328
+ $result = array();
329
+ $sql = count( $args ) ? $_wpdb->prepare( $query, $args ) : $query;
330
+ foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
331
+ $result[] = $this->getModel()->LoadData( $data );
332
+ }
333
+ return $result;
334
+ }
335
+
336
+ /**
337
+ * Table install query.
338
+ *
339
+ * @param string $prefix - (Optional) Table prefix.
340
+ * @return string - Must return SQL for creating table.
341
+ */
342
+ protected function _GetInstallQuery( $prefix = false ) {
343
+ $_wpdb = $this->connection;
344
+
345
+ $class = get_class( $this );
346
+ $copy = new $class( $this->connection );
347
+ $table_name = ($prefix) ? $this->GetWPTable() : $this->GetTable();
348
+ $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
349
+
350
+ foreach ( $this->GetColumns() as $key ) {
351
+ $sql .= ' ';
352
+ switch ( true ) {
353
+ case $key == $copy->_idkey:
354
+ $sql .= $key . ' BIGINT NOT NULL AUTO_INCREMENT,' . PHP_EOL;
355
+ break;
356
+ case is_integer( $copy->$key ):
357
+ $sql .= $key . ' BIGINT NOT NULL,' . PHP_EOL;
358
+ break;
359
+ case is_float( $copy->$key ):
360
+ $sql .= $key . ' DOUBLE NOT NULL,' . PHP_EOL;
361
+ break;
362
+ case is_string( $copy->$key ):
363
+ $maxlength = $key . '_maxlength';
364
+ if ( property_exists( $class, $maxlength ) ) {
365
+ $sql .= $key . ' VARCHAR(' . intval( $class::$$maxlength ) . ') NOT NULL,' . PHP_EOL;
366
+ } else {
367
+ $sql .= $key . ' TEXT NOT NULL,' . PHP_EOL;
368
+ }
369
+ break;
370
+ case is_bool( $copy->$key ):
371
+ $sql .= $key . ' BIT NOT NULL,' . PHP_EOL;
372
+ break;
373
+ case is_array( $copy->$key ):
374
+ case is_object( $copy->$key ):
375
+ $sql .= $key . ' LONGTEXT NOT NULL,' . PHP_EOL;
376
+ break;
377
+ }
378
+ }
379
+
380
+ $sql .= $this->GetTableOptions() . PHP_EOL;
381
+
382
+ $sql .= ')';
383
+
384
+ if ( ! empty( $_wpdb->charset ) ) {
385
+ $sql .= ' DEFAULT CHARACTER SET ' . $_wpdb->charset;
386
+ }
387
+
388
+ return $sql;
389
+ }
390
+
391
+ /**
392
+ * Must return SQL for removing table (at a minimum, it should be ` 'DROP TABLE ' . $this->_table `).
393
+ *
394
+ * @return string
395
+ */
396
+ protected function _GetUninstallQuery() {
397
+ return 'DROP TABLE ' . $this->GetTable();
398
+ }
399
+
400
+ /**
401
+ * Get Users user_login.
402
+ *
403
+ * @param int $_user_id - User ID.
404
+ * @return string comma separated users login
405
+ */
406
+ private function GetUserNames( $_user_id ) {
407
+ global $wpdb;
408
+
409
+ $user_names = '0';
410
+ if ( ! empty( $_user_id ) && 'null' != $_user_id ) {
411
+ $sql = 'SELECT user_login FROM ' . $wpdb->users . ' WHERE find_in_set(ID, @userId) > 0';
412
+ $wpdb->query( "SET @userId = $_user_id" );
413
+ $result = $wpdb->get_results( $sql, ARRAY_A );
414
+ $users_array = array();
415
+ foreach ( $result as $item ) {
416
+ $users_array[] = '"' . $item['user_login'] . '"';
417
+ }
418
+ $user_names = implode( ', ', $users_array );
419
+ }
420
+ return $user_names;
421
+ }
422
+
423
+ /**
424
+ * Function used in WSAL reporting extension.
425
+ *
426
+ * @param int $_site_id - Site ID.
427
+ * @param int $_user_id - User ID.
428
+ * @param string $_role_name - User role.
429
+ * @param int $_alert_code - Alert code.
430
+ * @param timestamp $_start_timestamp - From created_on.
431
+ * @param timestamp $_end_timestamp - To created_on.
432
+ * @param timestamp $_next_date - (Optional) Created on >.
433
+ * @param int $_limit - (Optional) Limit.
434
+ * @return array Report results
435
+ */
436
+ public function GetReporting( $_site_id, $_user_id, $_role_name, $_alert_code, $_start_timestamp, $_end_timestamp, $_next_date = null, $_limit = 0 ) {
437
+ global $wpdb;
438
+ $user_names = $this->GetUserNames( $_user_id );
439
+
440
+ $_wpdb = $this->connection;
441
+ $_wpdb->set_charset( $_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci' );
442
+ // Tables.
443
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
444
+ $table_meta = $meta->GetTable(); // Metadata.
445
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
446
+ $table_occ = $occurrence->GetTable(); // Occurrences.
447
+
448
+ $condition_date = ! empty( $_next_date ) ? ' AND occ.created_on < ' . $_next_date : '';
449
+
450
+ $sql = "SELECT DISTINCT
451
+ occ.id,
452
+ occ.alert_id,
453
+ occ.site_id,
454
+ occ.created_on,
455
+ replace(replace(replace((
456
+ SELECT t1.value FROM $table_meta AS t1 WHERE t1.name = 'CurrentUserRoles' AND t1.occurrence_id = occ.id LIMIT 1), '[', ''), ']', ''), '\\'', '') AS roles,
457
+ (SELECT replace(t2.value, '\"','') FROM $table_meta as t2 WHERE t2.name = 'ClientIP' AND t2.occurrence_id = occ.id LIMIT 1) AS ip,
458
+ (SELECT replace(t3.value, '\"', '') FROM $table_meta as t3 WHERE t3.name = 'UserAgent' AND t3.occurrence_id = occ.id LIMIT 1) AS ua,
459
+ COALESCE(
460
+ (SELECT replace(t4.value, '\"', '') FROM $table_meta as t4 WHERE t4.name = 'Username' AND t4.occurrence_id = occ.id LIMIT 1),
461
+ (SELECT replace(t5.value, '\"', '') FROM $table_meta as t5 WHERE t5.name = 'CurrentUserID' AND t5.occurrence_id = occ.id LIMIT 1)
462
+ ) as user_id
463
+ FROM $table_occ AS occ
464
+ JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
465
+ WHERE
466
+ (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
467
+ AND (@userId is NULL OR (
468
+ (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
469
+ OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
470
+ ))
471
+ AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
472
+ AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
473
+ ))
474
+ AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
475
+ AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
476
+ AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
477
+ {$condition_date}
478
+ ORDER BY
479
+ created_on DESC
480
+ ";
481
+
482
+ $_wpdb->query( "SET @siteId = $_site_id" );
483
+ $_wpdb->query( "SET @userId = $_user_id" );
484
+ $_wpdb->query( "SET @roleName = $_role_name" );
485
+ $_wpdb->query( "SET @alertCode = $_alert_code" );
486
+ $_wpdb->query( "SET @startTimestamp = $_start_timestamp" );
487
+ $_wpdb->query( "SET @endTimestamp = $_end_timestamp" );
488
+
489
+ if ( ! empty( $_limit ) ) {
490
+ $sql .= " LIMIT {$_limit}";
491
+ }
492
+ $results = $_wpdb->get_results( $sql );
493
+ if ( ! empty( $results ) ) {
494
+ foreach ( $results as $row ) {
495
+ $sql = "SELECT t6.ID FROM $wpdb->users AS t6 WHERE t6.user_login = \"$row->user_id\"";
496
+ $user_id = $wpdb->get_var( $sql );
497
+ if ( null == $user_id ) {
498
+ $sql = "SELECT t4.ID FROM $wpdb->users AS t4 WHERE t4.ID = \"$row->user_id\"";
499
+ $user_id = $wpdb->get_var( $sql );
500
+ }
501
+ $row->user_id = $user_id;
502
+ $results['lastDate'] = $row->created_on;
503
+ }
504
+ }
505
+
506
+ return $results;
507
+ }
508
+
509
+ /**
510
+ * Function used in WSAL reporting extension.
511
+ * Check if criteria are matching in the DB.
512
+ *
513
+ * @param mixed $criteria - Query conditions.
514
+ * @return int count of distinct values
515
+ */
516
+ public function CheckMatchReportCriteria( $criteria ) {
517
+ $_site_id = $criteria['siteId'];
518
+ $_user_id = $criteria['userId'];
519
+ $_role_name = $criteria['roleName'];
520
+ $_alert_code = $criteria['alertCode'];
521
+ $_start_timestamp = $criteria['startTimestamp'];
522
+ $_end_timestamp = $criteria['endTimestamp'];
523
+ $_ip_address = $criteria['ipAddress'];
524
+
525
+ $_wpdb = $this->connection;
526
+ $_wpdb->set_charset( $_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci' );
527
+ // Tables.
528
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
529
+ $table_meta = $meta->GetTable(); // Metadata.
530
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
531
+ $table_occ = $occurrence->GetTable(); // Occurrences.
532
+
533
+ $user_names = $this->GetUserNames( $_user_id );
534
+
535
+ $sql = "SELECT COUNT(DISTINCT occ.id) FROM $table_occ AS occ
536
+ JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
537
+ WHERE
538
+ (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
539
+ AND (@userId is NULL OR (
540
+ (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
541
+ OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
542
+ ))
543
+ AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
544
+ AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
545
+ ))
546
+ AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
547
+ AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
548
+ AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
549
+ AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
550
+ ";
551
+
552
+ $_wpdb->query( "SET @siteId = $_site_id" );
553
+ $_wpdb->query( "SET @userId = $_user_id" );
554
+ $_wpdb->query( "SET @roleName = $_role_name" );
555
+ $_wpdb->query( "SET @alertCode = $_alert_code" );
556
+ $_wpdb->query( "SET @startTimestamp = $_start_timestamp" );
557
+ $_wpdb->query( "SET @endTimestamp = $_end_timestamp" );
558
+ $_wpdb->query( "SET @ipAddress = $_ip_address" );
559
+
560
+ $count = (int) $_wpdb->get_var( $sql );
561
+ return $count;
562
+ }
563
+
564
+ /**
565
+ * Function used in WSAL reporting extension.
566
+ * List of unique IP addresses used by the same user.
567
+ *
568
+ * @param int $_site_id - Site ID.
569
+ * @param timestamp $_start_timestamp - From created_on.
570
+ * @param timestamp $_end_timestamp - To created_on.
571
+ * @param int $_user_id - (Optional) User ID.
572
+ * @param string $_role_name - (Optional) User role.
573
+ * @param string $_ip_address - (Optional) IP address.
574
+ * @param int $_alert_code - (Optional) Alert code.
575
+ * @param int $_limit - (Optional) Limit.
576
+ * @return array Report results grouped by IP and Username
577
+ */
578
+ public function GetReportGrouped( $_site_id, $_start_timestamp, $_end_timestamp, $_user_id = 'null', $_role_name = 'null', $_ip_address = 'null', $_alert_code = 'null', $_limit = 0 ) {
579
+ global $wpdb;
580
+ $user_names = $this->GetUserNames( $_user_id );
581
+
582
+ $_wpdb = $this->connection;
583
+ $_wpdb->set_charset( $_wpdb->dbh, 'utf8mb4', 'utf8mb4_general_ci' );
584
+ // Tables.
585
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
586
+ $table_meta = $meta->GetTable(); // Metadata.
587
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
588
+ $table_occ = $occurrence->GetTable(); // Occurrences.
589
+ // Get temp table `wsal_tmp_users`.
590
+ $tmp_users = new WSAL_Adapters_MySQL_TmpUser( $this->connection );
591
+ // If the table exist.
592
+ if ( $tmp_users->IsInstalled() ) {
593
+ $table_users = $tmp_users->GetTable(); // tmp_users.
594
+ $this->TempUsers( $table_users );
595
+ } else {
596
+ $table_users = $wpdb->users;
597
+ }
598
+
599
+ $sql = "SELECT DISTINCT *
600
+ FROM (SELECT DISTINCT
601
+ occ.site_id,
602
+ 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 ,
603
+ 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
604
+ FROM $table_occ AS occ
605
+ JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
606
+ WHERE
607
+ (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
608
+ AND (@userId is NULL OR (
609
+ (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
610
+ OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
611
+ ))
612
+ AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
613
+ AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
614
+ ))
615
+ AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
616
+ AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
617
+ AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
618
+ AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
619
+ HAVING user_login IS NOT NULL
620
+ UNION ALL
621
+ SELECT DISTINCT
622
+ occ.site_id,
623
+ CONVERT((SELECT u.user_login
624
+ FROM $table_meta as t2
625
+ JOIN $table_users AS u ON u.ID = replace(t2.value, '\"', '')
626
+ WHERE t2.name = 'CurrentUserID'
627
+ AND t2.occurrence_id = occ.id
628
+ GROUP BY u.ID
629
+ LIMIT 1) using UTF8) AS user_login,
630
+ 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
631
+ FROM $table_occ AS occ
632
+ JOIN $table_meta AS meta ON meta.occurrence_id = occ.id
633
+ WHERE
634
+ (@siteId is NULL OR find_in_set(occ.site_id, @siteId) > 0)
635
+ AND (@userId is NULL OR (
636
+ (meta.name = 'CurrentUserID' AND find_in_set(meta.value, @userId) > 0)
637
+ OR (meta.name = 'Username' AND replace(meta.value, '\"', '') IN ($user_names))
638
+ ))
639
+ AND (@roleName is NULL OR (meta.name = 'CurrentUserRoles'
640
+ AND replace(replace(replace(meta.value, ']', ''), '[', ''), '\\'', '') REGEXP @roleName
641
+ ))
642
+ AND (@alertCode is NULL OR find_in_set(occ.alert_id, @alertCode) > 0)
643
+ AND (@startTimestamp is NULL OR occ.created_on >= @startTimestamp)
644
+ AND (@endTimestamp is NULL OR occ.created_on <= @endTimestamp)
645
+ AND (@ipAddress is NULL OR (meta.name = 'ClientIP' AND find_in_set(meta.value, @ipAddress) > 0))
646
+ HAVING user_login IS NOT NULL) ip_logins
647
+ WHERE user_login NOT IN ('Website Visitor', 'Plugins', 'Plugin')
648
+ ORDER BY user_login ASC
649
+ ";
650
+ $_wpdb->query( "SET @siteId = $_site_id" );
651
+ $_wpdb->query( "SET @userId = $_user_id" );
652
+ $_wpdb->query( "SET @roleName = $_role_name" );
653
+ $_wpdb->query( "SET @alertCode = $_alert_code" );
654
+ $_wpdb->query( "SET @startTimestamp = $_start_timestamp" );
655
+ $_wpdb->query( "SET @endTimestamp = $_end_timestamp" );
656
+ $_wpdb->query( "SET @ipAddress = $_ip_address" );
657
+ if ( ! empty( $_limit ) ) {
658
+ $sql .= " LIMIT {$_limit}";
659
+ }
660
+
661
+ $grouped_types = array();
662
+ $results = $_wpdb->get_results( $sql );
663
+ if ( ! empty( $results ) ) {
664
+ foreach ( $results as $key => $row ) {
665
+ // Get the display_name only for the first row & if the user_login changed from the previous row.
666
+ if ( 0 == $key || ($key > 1 && $results[ ($key - 1) ]->user_login != $row->user_login) ) {
667
+ $sql = "SELECT t5.display_name FROM $wpdb->users AS t5 WHERE t5.user_login = \"$row->user_login\"";
668
+ $display_name = $wpdb->get_var( $sql );
669
+ }
670
+ $row->display_name = $display_name;
671
+
672
+ if ( ! isset( $grouped_types[ $row->user_login ] ) ) {
673
+ $grouped_types[ $row->user_login ] = array(
674
+ 'site_id' => $row->site_id,
675
+ 'user_login' => $row->user_login,
676
+ 'display_name' => $row->display_name,
677
+ 'ips' => array(),
678
+ );
679
+ }
680
+
681
+ $grouped_types[ $row->user_login ]['ips'][] = $row->ip;
682
+ }
683
+ }
684
+
685
+ return $grouped_types;
686
+ }
687
+
688
+ /**
689
+ * DELETE from table `tmp_users` and populate with users.
690
+ * It is used in the query of the above function.
691
+ *
692
+ * @param string $table_users - Table name.
693
+ */
694
+ private function TempUsers( $table_users ) {
695
+ $_wpdb = $this->connection;
696
+ $sql = "DELETE FROM $table_users";
697
+ $_wpdb->query( $sql );
698
+
699
+ $sql = "INSERT INTO $table_users (ID, user_login) VALUES " ;
700
+ $users = get_users(
701
+ array(
702
+ 'fields' => array( 'ID', 'user_login' ),
703
+ )
704
+ );
705
+ foreach ( $users as $user ) {
706
+ $sql .= '(' . $user->ID . ', \'' . $user->user_login . '\'), ';
707
+ }
708
+ $sql = rtrim( $sql, ', ' );
709
+ $_wpdb->query( $sql );
710
+ }
711
  }
classes/Adapters/MySQL/MetaAdapter.php CHANGED
@@ -1,84 +1,144 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * MySQL database Metadata class.
5
  *
6
  * MySQL wsal_metadata table used for to store the alert meta data:
7
  * username, user_roles, client_ip, user_agent, post_id, post_title, etc.
 
 
8
  */
9
- class WSAL_Adapters_MySQL_Meta extends WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_MetaInterface
10
- {
11
- protected $_table = 'wsal_metadata';
12
- protected $_idkey = 'id';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- public $id = 0;
15
- public $occurrence_id = 0;
16
- public $name = '';
17
- public static $name_maxlength = 100;
18
- public $value = array(); // force mixed type
 
 
 
19
 
20
- /**
21
- * @return WSAL_Models_Meta
22
- */
23
- public function GetModel()
24
- {
25
- return new WSAL_Models_Meta();
26
- }
27
-
28
- public function __construct($conn)
29
- {
30
- parent::__construct($conn);
31
- }
32
-
33
- /**
34
- * @return string SQL table options (constraints, foreign keys, indexes etc).
35
- */
36
- protected function GetTableOptions()
37
- {
38
- return parent::GetTableOptions() . ',' . PHP_EOL
39
- . ' KEY occurrence_name (occurrence_id,name)';
40
- }
41
 
42
- /**
43
- * Delete metadata by occurrence_id.
44
- * @param array $occurenceIds list of occurrence IDs
45
- */
46
- public function DeleteByOccurenceIds($occurenceIds)
47
- {
48
- if (!empty($occurenceIds)) {
49
- $sql = 'DELETE FROM ' . $this->GetTable() . ' WHERE occurrence_id IN (' . implode(',', $occurenceIds) . ')';
50
- // execute query
51
- parent::DeleteQuery($sql);
52
- }
53
- }
54
 
55
- /**
56
- * Load metadata by name and occurrence_id.
57
- * @param string $metaName metadata name
58
- * @param string $occurenceId metadata occurrence_id
59
- * @return WSAL_Meta[]
60
- */
61
- public function LoadByNameAndOccurenceId($metaName, $occurenceId)
62
- {
63
- return $this->Load('occurrence_id = %d AND name = %s', array($occurenceId, $metaName));
64
- }
65
 
66
- /**
67
- * Get distinct values of IPs.
68
- * @param int $limit (Optional) limit
69
- * @return array distinct values of IPs
70
- */
71
- public function GetMatchingIPs($limit = null)
72
- {
73
- $_wpdb = $this->connection;
74
- $sql = "SELECT DISTINCT value FROM {$this->GetTable()} WHERE name = \"ClientIP\"";
75
- if (!is_null($limit)) {
76
- $sql .= ' LIMIT ' . $limit;
77
- }
78
- $ips = $_wpdb->get_col($sql);
79
- foreach ($ips as $key => $ip) {
80
- $ips[$key] = str_replace('"', '', $ip);
81
- }
82
- return array_unique($ips);
83
- }
84
  }
1
  <?php
2
  /**
3
+ * Adapter: Meta data.
4
+ *
5
+ * MySQL database Metadata class.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * MySQL database Metadata class.
17
  *
18
  * MySQL wsal_metadata table used for to store the alert meta data:
19
  * username, user_roles, client_ip, user_agent, post_id, post_title, etc.
20
+ *
21
+ * @package Wsal
22
  */
23
+ class WSAL_Adapters_MySQL_Meta extends WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_MetaInterface {
24
+
25
+ /**
26
+ * Contains the table name.
27
+ *
28
+ * @var string
29
+ */
30
+ protected $_table = 'wsal_metadata';
31
+
32
+ /**
33
+ * Contains primary key column name, override as required.
34
+ *
35
+ * @var string
36
+ */
37
+ protected $_idkey = 'id';
38
+
39
+ /**
40
+ * Meta id.
41
+ *
42
+ * @var int
43
+ */
44
+ public $id = 0;
45
+
46
+ /**
47
+ * Occurrence id.
48
+ *
49
+ * @var int
50
+ */
51
+ public $occurrence_id = 0;
52
+
53
+ /**
54
+ * Meta name.
55
+ *
56
+ * @var string
57
+ */
58
+ public $name = '';
59
+
60
+ /**
61
+ * Meta name max length.
62
+ *
63
+ * @var int
64
+ */
65
+ public static $name_maxlength = 100;
66
+
67
+ /**
68
+ * Meta value.
69
+ *
70
+ * @var mix
71
+ */
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
+ /**
84
+ * Method: Constructor.
85
+ *
86
+ * @param array $conn - Connection array.
87
+ */
88
+ public function __construct( $conn ) {
89
+ parent::__construct( $conn );
90
+ }
91
 
92
+ /**
93
+ * SQL table options (constraints, foreign keys, indexes etc).
94
+ *
95
+ * @return string
96
+ */
97
+ protected function GetTableOptions() {
98
+ return parent::GetTableOptions() . ',' . PHP_EOL
99
+ . ' KEY occurrence_name (occurrence_id,name)';
100
+ }
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
+ /**
103
+ * Delete metadata by occurrence_id.
104
+ *
105
+ * @param array $occurence_ids - List of occurrence IDs.
106
+ */
107
+ public function DeleteByOccurenceIds( $occurence_ids ) {
108
+ if ( ! empty( $occurence_ids ) ) {
109
+ $sql = 'DELETE FROM ' . $this->GetTable() . ' WHERE occurrence_id IN (' . implode( ',', $occurence_ids ) . ')';
110
+ // Execute query.
111
+ parent::DeleteQuery( $sql );
112
+ }
113
+ }
114
 
115
+ /**
116
+ * Load metadata by name and occurrence_id.
117
+ *
118
+ * @param string $meta_name - Metadata name.
119
+ * @param string $occurence_id - Metadata occurrence_id.
120
+ * @return WSAL_Meta[]
121
+ */
122
+ public function LoadByNameAndOccurenceId( $meta_name, $occurence_id ) {
123
+ return $this->Load( 'occurrence_id = %d AND name = %s', array( $occurence_id, $meta_name ) );
124
+ }
125
 
126
+ /**
127
+ * Get distinct values of IPs.
128
+ *
129
+ * @param int $limit - (Optional) Limit.
130
+ * @return array - Distinct values of IPs.
131
+ */
132
+ public function GetMatchingIPs( $limit = null ) {
133
+ $_wpdb = $this->connection;
134
+ $sql = "SELECT DISTINCT value FROM {$this->GetTable()} WHERE name = \"ClientIP\"";
135
+ if ( ! is_null( $limit ) ) {
136
+ $sql .= ' LIMIT ' . $limit;
137
+ }
138
+ $ips = $_wpdb->get_col( $sql );
139
+ foreach ( $ips as $key => $ip ) {
140
+ $ips[ $key ] = str_replace( '"', '', $ip );
141
+ }
142
+ return array_unique( $ips );
143
+ }
144
  }
classes/Adapters/MySQL/OccurrenceAdapter.php CHANGED
@@ -1,253 +1,315 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * MySQL database Occurrence class.
5
  *
6
  * MySQL wsal_occurrences table used for to store the alerts.
 
 
7
  */
8
- class WSAL_Adapters_MySQL_Occurrence extends WSAL_Adapters_MySQL_ActiveRecord implements WSAL_Adapters_OccurrenceInterface
9
- {
10
- protected $_table = 'wsal_occurrences';
11
- protected $_idkey = 'id';
12
- protected $_meta;
13
-
14
- public $id = 0;
15
- public $site_id = 0;
16
- public $alert_id = 0;
17
- public $created_on = 0.0;
18
- public $is_read = false;
19
- public $is_migrated = false;
20
-
21
- public function __construct($conn)
22
- {
23
- parent::__construct($conn);
24
- }
25
-
26
- /**
27
- * @return string SQL table options (constraints, foreign keys, indexes etc).
28
- */
29
- protected function GetTableOptions()
30
- {
31
- return parent::GetTableOptions() . ',' . PHP_EOL
32
- . ' KEY site_alert_created (site_id,alert_id,created_on)';
33
- }
34
-
35
- /**
36
- * @return WSAL_Models_Occurrence
37
- */
38
- public function GetModel()
39
- {
40
- return new WSAL_Models_Occurrence();
41
- }
42
-
43
- /**
44
- * Returns metadata related to this event.
45
- * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
46
- * @return WSAL_Meta
47
- */
48
- public function GetMeta($occurence)
49
- {
50
- if (!isset($this->_meta)) {
51
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
52
- $this->_meta = $meta->Load('occurrence_id = %d', array($occurence->id));
53
- }
54
- return $this->_meta;
55
- }
56
-
57
- /**
58
- * Returns allmeta data related to this event.
59
- * @see WSAL_Adapters_MySQL_ActiveRecord::LoadArray()
60
- * @return WSAL_Meta[]
61
- */
62
- public function GetMultiMeta($occurence)
63
- {
64
- if (!isset($this->_meta)) {
65
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
66
- $this->_meta = $meta->LoadArray('occurrence_id = %d', array($occurence->id));
67
- }
68
- return $this->_meta;
69
- }
70
-
71
- /**
72
- * Loads a meta item given its name.
73
- * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
74
- * @param string $name Meta name.
75
- * @return WSAL_Meta The meta item, be sure to checked if it was loaded successfully.
76
- */
77
- public function GetNamedMeta($occurence, $name)
78
- {
79
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
80
- $this->_meta = $meta->Load('occurrence_id = %d AND name = %s', array($occurence->id, $name));
81
-
82
- return $this->_meta;
83
- }
84
-
85
- /**
86
- * Returns the first meta value from a given set of names. Useful when you have a mix of items that could provide a particular detail.
87
- * @param array $names List of meta names.
88
- * @return WSAL_Meta The first meta item that exists.
89
- */
90
- public function GetFirstNamedMeta($occurence, $names)
91
- {
92
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
93
- $query = '(' . str_repeat('name = %s OR ', count($names)).'0)';
94
- $query = 'occurrence_id = %d AND ' . $query . ' ORDER BY name DESC LIMIT 1';
95
- array_unshift($names, $occurence->id); // prepend args with occurrence id
96
-
97
- $this->_meta = $meta->Load($query, $names);
98
- return $meta->getModel()->LoadData($this->_meta);
99
-
100
- //TO DO: Do we want to reintroduce is loaded check/logic?
101
- //return $meta->IsLoaded() ? $meta : null;
102
- }
103
-
104
- /**
105
- * Returns newest unique occurrences.
106
- * @param integer $limit Maximum limit.
107
- * @return WSAL_Occurrence[]
108
- */
109
- public static function GetNewestUnique($limit = PHP_INT_MAX)
110
- {
111
- $temp = new self();
112
- return self::LoadMultiQuery('
113
- SELECT *, COUNT(alert_id) as count
114
- FROM (
115
- SELECT *
116
- FROM ' . $temp->GetTable() . '
117
- ORDER BY created_on DESC
118
- ) AS temp_table
119
- GROUP BY alert_id
120
- LIMIT %d
121
- ', array($limit));
122
- }
123
-
124
- /**
125
- * Gets occurences of the same type by IP and Username within specified time frame.
126
- * @param string $ipAddress
127
- * @param string $username
128
- * @param int $alertId Alert type we are lookign for
129
- * @param int $siteId
130
- * @param $startTime mktime
131
- * @param $endTime mktime
132
- * @return WSAL_Occurrence[]
133
- */
134
- public function CheckKnownUsers($args = array())
135
- {
136
- $tt2 = new WSAL_Adapters_MySQL_Meta($this->connection);
137
- return self::LoadMultiQuery(
138
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
139
- INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
140
- and ipMeta.name = "ClientIP"
141
- and ipMeta.value = %s
142
- INNER JOIN `' . $tt2->GetTable() . '` usernameMeta on usernameMeta.occurrence_id = occurrence.id
143
- and usernameMeta.name = "Username"
144
- and usernameMeta.value = %s
145
- WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
146
- AND (created_on BETWEEN %d AND %d)
147
- GROUP BY occurrence.id',
148
- $args
149
- );
150
- }
151
-
152
- /**
153
- * Gets occurences of the same type by IP within specified time frame.
154
- * @param string $ipAddress
155
- * @param int $alertId Alert type we are lookign for
156
- * @param int $siteId
157
- * @param $startTime mktime
158
- * @param $endTime mktime
159
- * @return WSAL_Occurrence[]
160
- */
161
- public function CheckUnKnownUsers($args = array())
162
- {
163
- $tt2 = new WSAL_Adapters_MySQL_Meta($this->connection);
164
- return self::LoadMultiQuery(
165
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
166
- INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
167
- and ipMeta.name = "ClientIP" and ipMeta.value = %s
168
- WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
169
- AND (created_on BETWEEN %d AND %d)
170
- GROUP BY occurrence.id',
171
- $args
172
- );
173
- }
174
-
175
- /**
176
- * Add conditions to the Query
177
- * @param string $query
178
- */
179
- protected function prepareOccurrenceQuery($query)
180
- {
181
- $searchQueryParameters = array();
182
- $searchConditions = array();
183
- $conditions = $query->getConditions();
184
-
185
- //BUG: not all conditions are occurence related. maybe it's just a field site_id. need seperate arrays
186
- if (!empty($conditions)) {
187
- $tmp = new WSAL_Adapters_MySQL_Meta($this->connection);
188
- $sWhereClause = "";
189
- foreach ($conditions as $field => $value) {
190
- if (!empty($sWhereClause)) {
191
- $sWhereClause .= " AND ";
192
- }
193
- $sWhereClause .= "name = %s AND value = %s";
194
- $searchQueryParameters[] = $field;
195
- $searchQueryParameters[] = $value;
196
- }
197
-
198
- $searchConditions[] = 'id IN (
199
- SELECT DISTINCT occurrence_id
200
- FROM ' . $tmp->GetTable() . '
201
- WHERE ' . $sWhereClause . '
202
- )';
203
- }
204
-
205
- //do something with search query parameters and search conditions - give them to the query adapter?
206
- return $searchConditions;
207
- }
208
-
209
- /**
210
- * Gets occurrence by Post_id.
211
- * @param int $post_id
212
- * @return WSAL_Occurrence[]
213
- */
214
- public function GetByPostID($post_id)
215
- {
216
- $tt2 = new WSAL_Adapters_MySQL_Meta($this->connection);
217
- return self::LoadMultiQuery(
218
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '`AS occurrence
219
- INNER JOIN `' . $tt2->GetTable() . '`AS postMeta ON postMeta.occurrence_id = occurrence.id
220
- and postMeta.name = "PostID"
221
- and postMeta.value = %d
222
- GROUP BY occurrence.id
223
- ORDER BY created_on DESC',
224
- array($post_id)
225
- );
226
- }
227
-
228
- /**
229
- * Gets occurences of the same type by IP within specified time frame.
230
- * @param string $ipAddress
231
- * @param string $username
232
- * @param int $alertId Alert type we are lookign for
233
- * @param int $siteId
234
- * @param $startTime mktime
235
- * @param $endTime mktime
236
- * @return WSAL_Occurrence[]
237
- */
238
- public function CheckAlert404($args = array())
239
- {
240
- $tt2 = new WSAL_Adapters_MySQL_Meta($this->connection);
241
- return self::LoadMultiQuery(
242
- 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
243
- INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
244
- and ipMeta.name = "ClientIP" and ipMeta.value = %s
245
- INNER JOIN `' . $tt2->GetTable() . '` usernameMeta on usernameMeta.occurrence_id = occurrence.id
246
- and usernameMeta.name = "Username" and usernameMeta.value = %s
247
- WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
248
- AND (created_on BETWEEN %d AND %d)
249
- GROUP BY occurrence.id',
250
- $args
251
- );
252
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  }
1
  <?php
2
  /**
3
+ * Adapter: Occurrence.
4
+ *
5
+ * MySQL database Occurrence class.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * MySQL database Occurrence class.
17
  *
18
  * MySQL wsal_occurrences table used for to store the alerts.
19
+ *
20
+ * @package Wsal
21
  */
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
+
31
+ /**
32
+ * Contains primary key column name, override as required.
33
+ *
34
+ * @var string
35
+ */
36
+ protected $_idkey = 'id';
37
+
38
+ /**
39
+ * Meta data.
40
+ *
41
+ * @var WSAL_Meta
42
+ */
43
+ protected $_meta;
44
+
45
+ /**
46
+ * Occurrence id.
47
+ *
48
+ * @var int
49
+ */
50
+ public $id = 0;
51
+
52
+ /**
53
+ * Site id.
54
+ *
55
+ * @var int
56
+ */
57
+ public $site_id = 0;
58
+
59
+ /**
60
+ * Alert id.
61
+ *
62
+ * @var int
63
+ */
64
+ public $alert_id = 0;
65
+
66
+ /**
67
+ * Created on.
68
+ *
69
+ * @var string
70
+ */
71
+ public $created_on = 0.0;
72
+
73
+ /**
74
+ * Is read?
75
+ *
76
+ * @var bool
77
+ */
78
+ public $is_read = false;
79
+
80
+ /**
81
+ * Is migrated?
82
+ *
83
+ * @var bool
84
+ */
85
+ public $is_migrated = false;
86
+
87
+ /**
88
+ * Method: Constructor.
89
+ *
90
+ * @param array $conn - Connection array.
91
+ */
92
+ public function __construct( $conn ) {
93
+ parent::__construct( $conn );
94
+ }
95
+
96
+ /**
97
+ * SQL table options (constraints, foreign keys, indexes etc).
98
+ *
99
+ * @return string
100
+ */
101
+ protected function GetTableOptions() {
102
+ return parent::GetTableOptions() . ',' . PHP_EOL
103
+ . ' KEY site_alert_created (site_id,alert_id,created_on)';
104
+ }
105
+
106
+ /**
107
+ * Returns the model class for adapter.
108
+ *
109
+ * @return WSAL_Models_Occurrence
110
+ */
111
+ public function GetModel() {
112
+ return new WSAL_Models_Occurrence();
113
+ }
114
+
115
+ /**
116
+ * Returns metadata related to this event.
117
+ *
118
+ * @param object $occurence - Occurrence model instance.
119
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
120
+ * @return WSAL_Meta
121
+ */
122
+ public function GetMeta( $occurence ) {
123
+ if ( ! isset( $this->_meta ) ) {
124
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
125
+ $this->_meta = $meta->Load( 'occurrence_id = %d', array( $occurence->id ) );
126
+ }
127
+ return $this->_meta;
128
+ }
129
+
130
+ /**
131
+ * Returns allmeta data related to this event.
132
+ *
133
+ * @param object $occurence - Occurrence model instance.
134
+ * @see WSAL_Adapters_MySQL_ActiveRecord::LoadArray()
135
+ * @return WSAL_Meta[]
136
+ */
137
+ public function GetMultiMeta( $occurence ) {
138
+ if ( ! isset( $this->_meta ) ) {
139
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
140
+ $this->_meta = $meta->LoadArray( 'occurrence_id = %d', array( $occurence->id ) );
141
+ }
142
+ return $this->_meta;
143
+ }
144
+
145
+ /**
146
+ * Loads a meta item given its name.
147
+ *
148
+ * @param object $occurence - Occurrence model instance.
149
+ * @param string $name - Meta name.
150
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
151
+ * @return WSAL_Meta The meta item, be sure to checked if it was loaded successfully.
152
+ */
153
+ public function GetNamedMeta( $occurence, $name ) {
154
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
155
+ $this->_meta = $meta->Load( 'occurrence_id = %d AND name = %s', array( $occurence->id, $name ) );
156
+ return $this->_meta;
157
+ }
158
+
159
+ /**
160
+ * Returns the first meta value from a given set of names. Useful when you have a mix of items that could provide a particular detail.
161
+ *
162
+ * @param object $occurence - Occurrence model instance.
163
+ * @param array $names - List of meta names.
164
+ * @return WSAL_Meta The first meta item that exists.
165
+ */
166
+ public function GetFirstNamedMeta( $occurence, $names ) {
167
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
168
+ $query = '(' . str_repeat( 'name = %s OR ', count( $names ) ) . '0)';
169
+ $query = 'occurrence_id = %d AND ' . $query . ' ORDER BY name DESC LIMIT 1';
170
+ array_unshift( $names, $occurence->id ); // Prepend args with occurrence id.
171
+
172
+ $this->_meta = $meta->Load( $query, $names );
173
+ return $meta->getModel()->LoadData( $this->_meta );
174
+
175
+ // TO DO: Do we want to reintroduce is loaded check/logic?
176
+ // return $meta->IsLoaded() ? $meta : null;
177
+ }
178
+
179
+ /**
180
+ * Returns newest unique occurrences.
181
+ *
182
+ * @param integer $limit Maximum limit.
183
+ * @return WSAL_Occurrence[]
184
+ */
185
+ public static function GetNewestUnique( $limit = PHP_INT_MAX ) {
186
+ $temp = new self();
187
+ return self::LoadMultiQuery('
188
+ SELECT *, COUNT(alert_id) as count
189
+ FROM (
190
+ SELECT *
191
+ FROM ' . $temp->GetTable() . '
192
+ ORDER BY created_on DESC
193
+ ) AS temp_table
194
+ GROUP BY alert_id
195
+ LIMIT %d
196
+ ', array( $limit )
197
+ );
198
+ }
199
+
200
+ /**
201
+ * Gets occurences of the same type by IP and Username within specified time frame.
202
+ *
203
+ * @param array $args - User arguments.
204
+ * @return WSAL_Occurrence[]
205
+ */
206
+ public function CheckKnownUsers( $args = array() ) {
207
+ $tt2 = new WSAL_Adapters_MySQL_Meta( $this->connection );
208
+ return self::LoadMultiQuery(
209
+ 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
210
+ INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
211
+ and ipMeta.name = "ClientIP"
212
+ and ipMeta.value = %s
213
+ INNER JOIN `' . $tt2->GetTable() . '` usernameMeta on usernameMeta.occurrence_id = occurrence.id
214
+ and usernameMeta.name = "Username"
215
+ and usernameMeta.value = %s
216
+ WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
217
+ AND (created_on BETWEEN %d AND %d)
218
+ GROUP BY occurrence.id',
219
+ $args
220
+ );
221
+ }
222
+
223
+ /**
224
+ * Gets occurences of the same type by IP within specified time frame.
225
+ *
226
+ * @param array $args - User arguments.
227
+ * @return WSAL_Occurrence[]
228
+ */
229
+ public function CheckUnKnownUsers( $args = array() ) {
230
+ $tt2 = new WSAL_Adapters_MySQL_Meta( $this->connection );
231
+ return self::LoadMultiQuery(
232
+ 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
233
+ INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
234
+ and ipMeta.name = "ClientIP" and ipMeta.value = %s
235
+ WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
236
+ AND (created_on BETWEEN %d AND %d)
237
+ GROUP BY occurrence.id',
238
+ $args
239
+ );
240
+ }
241
+
242
+ /**
243
+ * Add conditions to the Query
244
+ *
245
+ * @param string $query - Query.
246
+ */
247
+ protected function prepareOccurrenceQuery( $query ) {
248
+ $search_query_parameters = array();
249
+ $search_conditions = array();
250
+ $conditions = $query->getConditions();
251
+
252
+ // BUG: not all conditions are occurence related. maybe it's just a field site_id. need seperate arrays.
253
+ if ( ! empty( $conditions ) ) {
254
+ $tmp = new WSAL_Adapters_MySQL_Meta( $this->connection );
255
+ $s_where_clause = '';
256
+ foreach ( $conditions as $field => $value ) {
257
+ if ( ! empty( $s_where_clause ) ) {
258
+ $s_where_clause .= ' AND ';
259
+ }
260
+ $s_where_clause .= 'name = %s AND value = %s';
261
+ $search_query_parameters[] = $field;
262
+ $search_query_parameters[] = $value;
263
+ }
264
+
265
+ $search_conditions[] = 'id IN (
266
+ SELECT DISTINCT occurrence_id
267
+ FROM ' . $tmp->GetTable() . '
268
+ WHERE ' . $s_where_clause . '
269
+ )';
270
+ }
271
+
272
+ // Do something with search query parameters and search conditions - give them to the query adapter?
273
+ return $search_conditions;
274
+ }
275
+
276
+ /**
277
+ * Gets occurrence by Post_id.
278
+ *
279
+ * @param int $post_id - Post ID.
280
+ * @return WSAL_Occurrence[]
281
+ */
282
+ public function GetByPostID( $post_id ) {
283
+ $tt2 = new WSAL_Adapters_MySQL_Meta( $this->connection );
284
+ return self::LoadMultiQuery(
285
+ 'SELECT occurrence.* FROM `' . $this->GetTable() . '`AS occurrence
286
+ INNER JOIN `' . $tt2->GetTable() . '`AS postMeta ON postMeta.occurrence_id = occurrence.id
287
+ and postMeta.name = "PostID"
288
+ and postMeta.value = %d
289
+ GROUP BY occurrence.id
290
+ ORDER BY created_on DESC',
291
+ array( $post_id )
292
+ );
293
+ }
294
+
295
+ /**
296
+ * Gets occurences of the same type by IP within specified time frame.
297
+ *
298
+ * @param array $args - Query Arguments.
299
+ * @return WSAL_Occurrence[]
300
+ */
301
+ public function CheckAlert404( $args = array() ) {
302
+ $tt2 = new WSAL_Adapters_MySQL_Meta( $this->connection );
303
+ return self::LoadMultiQuery(
304
+ 'SELECT occurrence.* FROM `' . $this->GetTable() . '` occurrence
305
+ INNER JOIN `' . $tt2->GetTable() . '` ipMeta on ipMeta.occurrence_id = occurrence.id
306
+ and ipMeta.name = "ClientIP" and ipMeta.value = %s
307
+ INNER JOIN `' . $tt2->GetTable() . '` usernameMeta on usernameMeta.occurrence_id = occurrence.id
308
+ and usernameMeta.name = "Username" and usernameMeta.value = %s
309
+ WHERE occurrence.alert_id = %d AND occurrence.site_id = %d
310
+ AND (created_on BETWEEN %d AND %d)
311
+ GROUP BY occurrence.id',
312
+ $args
313
+ );
314
+ }
315
  }
classes/Adapters/MySQL/OptionAdapter.php CHANGED
@@ -1,116 +1,169 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * MySQL database Option class.
5
  *
6
  * MySQL wsal_options table used for to store the plugin settings and Add-Ons settings.
 
 
7
  */
8
- class WSAL_Adapters_MySQL_Option extends WSAL_Adapters_MySQL_ActiveRecord
9
- {
10
- protected $_table = 'wsal_options';
11
- protected $_idkey = 'id';
12
-
13
- public $id = 0;
14
- public $option_name = '';
15
- public static $option_name_maxlength = 100;
16
- public $option_value = '';
17
-
18
- public function __construct($conn)
19
- {
20
- parent::__construct($conn);
21
- }
22
-
23
- /**
24
- * @return WSAL_Models_Option
25
- */
26
- public function GetModel()
27
- {
28
- return new WSAL_Models_Option();
29
- }
30
-
31
- /**
32
- * Get option by name.
33
- * @param string $name option_name
34
- * @return string|null option value
35
- */
36
- public function GetNamedOption($name)
37
- {
38
- if ($this->IsInstalled()) {
39
- return $this->Load('option_name = %s', array($name));
40
- } else {
41
- return null;
42
- }
43
- }
44
-
45
- /**
46
- * Get options by prefix (notifications stored in json format).
47
- * @param string $opt_prefix prefix
48
- * @return array|null options
49
- */
50
- public function GetNotificationsSetting($opt_prefix)
51
- {
52
- if ($this->IsInstalled()) {
53
- return $this->LoadArray('option_name LIKE %s', array($opt_prefix."%"));
54
- } else {
55
- return null;
56
- }
57
- }
58
-
59
- /**
60
- * Get option by id (notifications stored in json format).
61
- * @param int $id option ID
62
- * @return string|null option
63
- */
64
- public function GetNotification($id)
65
- {
66
- if ($this->IsInstalled()) {
67
- return $this->Load('id = %d', array($id));
68
- } else {
69
- return null;
70
- }
71
- }
72
-
73
- /**
74
- * Delete option by name.
75
- * @param string $name option_name
76
- * @return boolean.
77
- */
78
- public function DeleteByName($name)
79
- {
80
- if (!empty($name)) {
81
- $sql = "DELETE FROM " . $this->GetTable() . " WHERE option_name = '". $name ."'";
82
- // execute query
83
- return parent::DeleteQuery($sql);
84
- } else {
85
- return false;
86
- }
87
- }
88
-
89
- /**
90
- * Delete options start with prefix.
91
- * @param string $opt_prefix prefix
92
- * @return boolean.
93
- */
94
- public function DeleteByPrefix($opt_prefix)
95
- {
96
- if (!empty($opt_prefix)) {
97
- $sql = "DELETE FROM " . $this->GetTable() . " WHERE option_name LIKE '". $opt_prefix ."%'";
98
- // execute query
99
- return parent::DeleteQuery($sql);
100
- } else {
101
- return false;
102
- }
103
- }
104
-
105
- /**
106
- * Number of options start with prefix.
107
- * @param string $opt_prefix prefix
108
- * @return integer Indicates the number of items.
109
- */
110
- public function CountNotifications($opt_prefix)
111
- {
112
- $_wpdb = $this->connection;
113
- $sql = "SELECT COUNT(id) FROM " . $this->GetTable() . " WHERE option_name LIKE '". $opt_prefix ."%'";
114
- return (int)$_wpdb->get_var($sql);
115
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
1
  <?php
2
  /**
3
+ * Adapter: Option.
4
+ *
5
+ * MySQL database Option class.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * MySQL database Option class.
17
  *
18
  * MySQL wsal_options table used for to store the plugin settings and Add-Ons settings.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Adapters_MySQL_Option extends WSAL_Adapters_MySQL_ActiveRecord {
23
+
24
+ /**
25
+ * Contains the table name.
26
+ *
27
+ * @var string
28
+ */
29
+ protected $_table = 'wsal_options';
30
+
31
+ /**
32
+ * Contains primary key column name, override as required.
33
+ *
34
+ * @var string
35
+ */
36
+ protected $_idkey = 'id';
37
+
38
+ /**
39
+ * Option id.
40
+ *
41
+ * @var int
42
+ */
43
+ public $id = 0;
44
+
45
+ /**
46
+ * Option name.
47
+ *
48
+ * @var string
49
+ */
50
+ public $option_name = '';
51
+
52
+ /**
53
+ * Option name max length.
54
+ *
55
+ * @var int
56
+ */
57
+ public static $option_name_maxlength = 100;
58
+
59
+ /**
60
+ * Option value.
61
+ *
62
+ * @var mix
63
+ */
64
+ public $option_value = '';
65
+
66
+ /**
67
+ * Method: Constructor.
68
+ *
69
+ * @param array $conn - Connection array.
70
+ */
71
+ public function __construct( $conn ) {
72
+ parent::__construct( $conn );
73
+ }
74
+
75
+ /**
76
+ * Returns the model class for adapter.
77
+ *
78
+ * @return WSAL_Models_Occurrence
79
+ */
80
+ public function GetModel() {
81
+ return new WSAL_Models_Option();
82
+ }
83
+
84
+ /**
85
+ * Get option by name.
86
+ *
87
+ * @param string $name - Option name.
88
+ * @return string|null - Option value.
89
+ */
90
+ public function GetNamedOption( $name ) {
91
+ if ( $this->IsInstalled() ) {
92
+ return $this->Load( 'option_name = %s', array( $name ) );
93
+ } else {
94
+ return null;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Get options by prefix (notifications stored in json format).
100
+ *
101
+ * @param string $opt_prefix - Prefix.
102
+ * @return array|null - Options.
103
+ */
104
+ public function GetNotificationsSetting( $opt_prefix ) {
105
+ if ( $this->IsInstalled() ) {
106
+ return $this->LoadArray( 'option_name LIKE %s', array( $opt_prefix . '%' ) );
107
+ } else {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Get option by id (notifications stored in json format).
114
+ *
115
+ * @param int $id - Option ID.
116
+ * @return string|null - Option.
117
+ */
118
+ public function GetNotification( $id ) {
119
+ if ( $this->IsInstalled() ) {
120
+ return $this->Load( 'id = %d', array( $id ) );
121
+ } else {
122
+ return null;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Delete option by name.
128
+ *
129
+ * @param string $name - Option name.
130
+ * @return boolean.
131
+ */
132
+ public function DeleteByName( $name ) {
133
+ if ( ! empty( $name ) ) {
134
+ $sql = 'DELETE FROM ' . $this->GetTable() . " WHERE option_name = '" . $name . "'";
135
+ // Execute query.
136
+ return parent::DeleteQuery( $sql );
137
+ } else {
138
+ return false;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Delete options start with prefix.
144
+ *
145
+ * @param string $opt_prefix - Prefix.
146
+ * @return boolean.
147
+ */
148
+ public function DeleteByPrefix( $opt_prefix ) {
149
+ if ( ! empty( $opt_prefix ) ) {
150
+ $sql = 'DELETE FROM ' . $this->GetTable() . " WHERE option_name LIKE '" . $opt_prefix . "%'";
151
+ // Execute query.
152
+ return parent::DeleteQuery( $sql );
153
+ } else {
154
+ return false;
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Number of options start with prefix.
160
+ *
161
+ * @param string $opt_prefix - Prefix.
162
+ * @return integer Indicates the number of items.
163
+ */
164
+ public function CountNotifications( $opt_prefix ) {
165
+ $_wpdb = $this->connection;
166
+ $sql = 'SELECT COUNT(id) FROM ' . $this->GetTable() . " WHERE option_name LIKE '" . $opt_prefix . "%'";
167
+ return (int) $_wpdb->get_var( $sql );
168
+ }
169
  }
classes/Adapters/MySQL/QueryAdapter.php CHANGED
@@ -1,269 +1,292 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * MySQL database Query class.
5
  *
6
  * The SQL query is created in this class, here the SQL is filled with
7
  * the arguments.
 
 
8
  */
9
- class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface
10
- {
11
- protected $connection;
 
 
 
 
 
12
 
13
- public function __construct($conn)
14
- {
15
- $this->connection = $conn;
16
- }
 
 
 
 
17
 
18
- /**
19
- * Get the SQL filled with the args.
20
- * @param object $query query object
21
- * @param array $args args of the query
22
- * @return string Generated sql.
23
- */
24
- protected function GetSql($query, &$args = array())
25
- {
26
- $conditions = $query->getConditions();
27
- $searchCondition = $this->SearchCondition($query);
28
- $sWhereClause = "";
29
- foreach ($conditions as $fieldName => $fieldValue) {
30
- if (empty($sWhereClause)) {
31
- $sWhereClause .= " WHERE ";
32
- } else {
33
- $sWhereClause .= " AND ";
34
- }
35
 
36
- if (is_array($fieldValue)) {
37
- $subWhereClause = "(";
38
- foreach ($fieldValue as $orFieldName => $orFieldValue) {
39
- if (is_array($orFieldValue)) {
40
- foreach ($orFieldValue as $value) {
41
- if ($subWhereClause != '(') {
42
- $subWhereClause .= " OR ";
43
- }
44
- $subWhereClause .= $orFieldName;
45
- $args[] = $value;
46
- }
47
- } else {
48
- if ($subWhereClause != '(') {
49
- $subWhereClause .= " OR ";
50
- }
51
- $subWhereClause .= $orFieldName;
52
- $args[] = $orFieldValue;
53
- }
54
- }
55
- $subWhereClause .= ")";
56
- $sWhereClause .= $subWhereClause;
57
- } else {
58
- $sWhereClause .= $fieldName;
59
- $args[] = $fieldValue;
60
- }
61
- }
62
 
63
- $fromDataSets = $query->getFrom();
64
- $columns = $query->getColumns();
65
- $orderBys = $query->getOrderBy();
66
 
67
- $sLimitClause = "";
68
- if ($query->getLimit()) {
69
- $sLimitClause .= " LIMIT ";
70
- if ($query->getOffset()) {
71
- $sLimitClause .= $query->getOffset() . ", ";
72
- }
73
- $sLimitClause .= $query->getLimit();
74
- }
75
- $joinClause = '';
76
- if ($query->hasMetaJoin()) {
77
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
78
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($this->connection);
79
- $joinClause = ' LEFT JOIN '. $meta->GetTable() .' AS meta ON meta.occurrence_id = '. $occurrence->GetTable() .'.id ';
80
- }
81
- $fields = (empty($columns))? $fromDataSets[0] . '.*' : implode(',', $columns);
82
- if (!empty($searchCondition)) {
83
- $args[] = $searchCondition['args'];
84
- }
85
 
86
- $sql = 'SELECT ' . $fields
87
- . ' FROM ' . implode(',', $fromDataSets)
88
- . $joinClause
89
- . $sWhereClause
90
- . (!empty($searchCondition) ? (empty($sWhereClause) ? " WHERE ".$searchCondition['sql'] : " AND ".$searchCondition['sql']) : '')
91
- // @todo GROUP BY goes here
92
- . (!empty($orderBys) ? (' ORDER BY ' . implode(', ', array_keys($orderBys)) . ' ' . implode(', ', array_values($orderBys))) : '')
93
- . $sLimitClause;
94
- return $sql;
95
- }
96
 
97
- /**
98
- * Get an instance of the ActiveRecord Adapter.
99
- * @return WSAL_Adapters_MySQL_ActiveRecord
100
- */
101
- protected function getActiveRecordAdapter()
102
- {
103
- return new WSAL_Adapters_MySQL_ActiveRecord($this->connection);
104
- }
105
 
106
- /**
107
- * Execute query and return data as $ar_cls objects.
108
- * @param object $query query object
109
- * @return WSAL_Models_ActiveRecord[]
110
- */
111
- public function Execute($query)
112
- {
113
- $args = array();
114
- $sql = $this->GetSql($query, $args);
115
 
116
- $occurenceAdapter = $query->getConnector()->getAdapter("Occurrence");
117
 
118
- if (in_array($occurenceAdapter->GetTable(), $query->getFrom())) {
119
- return $occurenceAdapter->LoadMulti($sql, $args);
120
- } else {
121
- return $this->getActiveRecordAdapter()->LoadMulti($sql, $args);
122
- }
123
- }
124
 
125
- /**
126
- * Count query
127
- * @param object $query query object
128
- * @return integer counting records.
129
- */
130
- public function Count($query)
131
- {
132
- // back up columns, use COUNT as default column and generate sql
133
- $cols = $query->getColumns();
134
- $query->clearColumns();
135
- $query->addColumn('COUNT(*)');
136
 
137
- $args = array();
138
- $sql = $this->GetSql($query, $args);
139
 
140
- // restore columns
141
- $query->setColumns($cols);
142
- // execute query and return result
143
- return $this->getActiveRecordAdapter()->CountQuery($sql, $args);
144
- }
145
 
146
- /**
147
- * Count DELETE query
148
- * @param object $query query object
149
- * @return integer counting records.
150
- */
151
- public function CountDelete($query)
152
- {
153
- $result = $this->GetSqlDelete($query, true);
154
- // execute query and return result
155
- return $this->getActiveRecordAdapter()->CountQuery($result['sql'], $result['args']);
156
- }
157
 
158
- /**
159
- * Query for deleting records
160
- * @param object $query query object.
161
- */
162
- public function Delete($query)
163
- {
164
- $result = $this->GetSqlDelete($query);
165
- $this->DeleteMetas($query, $result['args']);
166
- return $this->getActiveRecordAdapter()->DeleteQuery($result['sql'], $result['args']);
167
- }
168
 
169
- /**
170
- * Load occurrence IDs then delete Metadata by occurrence_id
171
- * @param object $query query object
172
- * @param array $args args of the query
173
- */
174
- public function DeleteMetas($query, $args)
175
- {
176
- // back up columns, use COUNT as default column and generate sql
177
- $cols = $query->getColumns();
178
- $query->clearColumns();
179
- $query->addColumn('id');
180
- $sql = $this->GetSql($query);
181
- // restore columns
182
- $query->setColumns($cols);
183
 
184
- $_wpdb = $this->connection;
185
- $occ_ids = array();
186
- $sql = (!empty($args) ? $_wpdb->prepare($sql, $args) : $sql);
187
- foreach ($_wpdb->get_results($sql, ARRAY_A) as $data) {
188
- $occ_ids[] = $data['id'];
189
- }
190
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
191
- $meta->DeleteByOccurenceIds($occ_ids);
192
- }
193
 
194
- /**
195
- * Get the DELETE query SQL filled with the args.
196
- * @param object $query query object
197
- * @param array $args args of the query
198
- * @return string Generated sql.
199
- */
200
- public function GetSqlDelete($query, $getCount = false)
201
- {
202
- $result = array();
203
- $args = array();
204
- // back up columns, remove them for DELETE and generate sql
205
- $cols = $query->getColumns();
206
- $query->clearColumns();
207
 
208
- $conditions = $query->getConditions();
209
 
210
- $sWhereClause = "";
211
- foreach ($conditions as $fieldName => $fieldValue) {
212
- if (empty($sWhereClause)) {
213
- $sWhereClause .= " WHERE ";
214
- } else {
215
- $sWhereClause .= " AND ";
216
- }
217
- $sWhereClause .= $fieldName;
218
- $args[] = $fieldValue;
219
- }
220
 
221
- $fromDataSets = $query->getFrom();
222
- $orderBys = $query->getOrderBy();
223
 
224
- $sLimitClause = "";
225
- if ($query->getLimit()) {
226
- $sLimitClause .= " LIMIT ";
227
- if ($query->getOffset()) {
228
- $sLimitClause .= $query->getOffset() . ", ";
229
- }
230
- $sLimitClause .= $query->getLimit();
231
- }
232
- $result['sql'] = ($getCount ? 'SELECT COUNT(*) FROM ' : 'DELETE FROM ')
233
- . implode(',', $fromDataSets)
234
- . $sWhereClause
235
- . (!empty($orderBys) ? (' ORDER BY ' . implode(', ', array_keys($orderBys)) . ' ' . implode(', ', array_values($orderBys))) : '')
236
- . $sLimitClause;
237
- $result['args'] = $args;
238
- // restore columns
239
- $query->setColumns($cols);
240
 
241
- return $result;
242
- }
243
 
244
- /**
245
- * Search by alert code OR by Metadata value.
246
- * @param object $query query object
247
- */
248
- public function SearchCondition($query)
249
- {
250
- $condition = $query->getSearchCondition();
251
- if (empty($condition)) {
252
- return null;
253
- }
254
- $searchConditions = array();
255
- $meta = new WSAL_Adapters_MySQL_Meta($this->connection);
256
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($this->connection);
257
- if (is_numeric($condition) && strlen($condition) == 4) {
258
- $searchConditions['sql'] = $occurrence->GetTable() .'.alert_id LIKE %s';
259
- } else {
260
- $searchConditions['sql'] = $occurrence->GetTable() .'.id IN (
261
- SELECT DISTINCT occurrence_id
262
- FROM ' . $meta->GetTable() . '
263
- WHERE TRIM(BOTH "\"" FROM value) LIKE %s
264
- )';
265
- }
266
- $searchConditions['args'] = "%". $condition. "%";
267
- return $searchConditions;
268
- }
269
  }
1
  <?php
2
  /**
3
+ * Adapter: Query.
4
+ *
5
+ * MySQL database Query class.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * MySQL database Query class.
17
  *
18
  * The SQL query is created in this class, here the SQL is filled with
19
  * the arguments.
20
+ *
21
+ * @package Wsal
22
  */
23
+ class WSAL_Adapters_MySQL_Query implements WSAL_Adapters_QueryInterface {
24
+
25
+ /**
26
+ * DB Connection
27
+ *
28
+ * @var array
29
+ */
30
+ protected $connection;
31
 
32
+ /**
33
+ * Method: Constructor.
34
+ *
35
+ * @param array $conn - Connection array.
36
+ */
37
+ public function __construct( $conn ) {
38
+ $this->connection = $conn;
39
+ }
40
 
41
+ /**
42
+ * Get the SQL filled with the args.
43
+ *
44
+ * @param object $query - Query object.
45
+ * @param array $args - Args of the query.
46
+ * @return string Generated sql.
47
+ */
48
+ protected function GetSql( $query, &$args = array() ) {
49
+ $conditions = $query->getConditions();
50
+ $search_condition = $this->SearchCondition( $query );
51
+ $s_where_clause = '';
52
+ foreach ( $conditions as $field_name => $field_value ) {
53
+ if ( empty( $s_where_clause ) ) {
54
+ $s_where_clause .= ' WHERE ';
55
+ } else {
56
+ $s_where_clause .= ' AND ';
57
+ }
58
 
59
+ if ( is_array( $field_value ) ) {
60
+ $sub_where_clause = '(';
61
+ foreach ( $field_value as $or_field_name => $or_field_value ) {
62
+ if ( is_array( $or_field_value ) ) {
63
+ foreach ( $or_field_value as $value ) {
64
+ if ( '(' != $sub_where_clause ) {
65
+ $sub_where_clause .= ' OR ';
66
+ }
67
+ $sub_where_clause .= $or_field_name;
68
+ $args[] = $value;
69
+ }
70
+ } else {
71
+ if ( '(' != $sub_where_clause ) {
72
+ $sub_where_clause .= ' OR ';
73
+ }
74
+ $sub_where_clause .= $or_field_name;
75
+ $args[] = $or_field_value;
76
+ }
77
+ }
78
+ $sub_where_clause .= ')';
79
+ $s_where_clause .= $sub_where_clause;
80
+ } else {
81
+ $s_where_clause .= $field_name;
82
+ $args[] = $field_value;
83
+ }
84
+ }
85
 
86
+ $from_data_sets = $query->getFrom();
87
+ $columns = $query->getColumns();
88
+ $order_bys = $query->getOrderBy();
89
 
90
+ $s_limit_clause = '';
91
+ if ( $query->getLimit() ) {
92
+ $s_limit_clause .= ' LIMIT ';
93
+ if ( $query->getOffset() ) {
94
+ $s_limit_clause .= $query->getOffset() . ', ';
95
+ }
96
+ $s_limit_clause .= $query->getLimit();
97
+ }
98
+ $join_clause = '';
99
+ if ( $query->hasMetaJoin() ) {
100
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
101
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
102
+ $join_clause = ' LEFT JOIN ' . $meta->GetTable() . ' AS meta ON meta.occurrence_id = ' . $occurrence->GetTable() . '.id ';
103
+ }
104
+ $fields = (empty( $columns )) ? $from_data_sets[0] . '.*' : implode( ',', $columns );
105
+ if ( ! empty( $search_condition ) ) {
106
+ $args[] = $search_condition['args'];
107
+ }
108
 
109
+ $sql = 'SELECT ' . $fields
110
+ . ' FROM ' . implode( ',', $from_data_sets )
111
+ . $join_clause
112
+ . $s_where_clause
113
+ . ( ! empty( $search_condition ) ? (empty( $s_where_clause ) ? ' WHERE ' . $search_condition['sql'] : ' AND ' . $search_condition['sql']) : '')
114
+ // @todo GROUP BY goes here
115
+ . ( ! empty( $order_bys ) ? (' ORDER BY ' . implode( ', ', array_keys( $order_bys ) ) . ' ' . implode( ', ', array_values( $order_bys ) )) : '')
116
+ . $s_limit_clause;
117
+ return $sql;
118
+ }
119
 
120
+ /**
121
+ * Get an instance of the ActiveRecord Adapter.
122
+ *
123
+ * @return WSAL_Adapters_MySQL_ActiveRecord
124
+ */
125
+ protected function getActiveRecordAdapter() {
126
+ return new WSAL_Adapters_MySQL_ActiveRecord( $this->connection );
127
+ }
128
 
129
+ /**
130
+ * Execute query and return data as $ar_cls objects.
131
+ *
132
+ * @param object $query - Query object.
133
+ * @return WSAL_Models_ActiveRecord[]
134
+ */
135
+ public function Execute( $query ) {
136
+ $args = array();
137
+ $sql = $this->GetSql( $query, $args );
138
 
139
+ $occurence_adapter = $query->getConnector()->getAdapter( 'Occurrence' );
140
 
141
+ if ( in_array( $occurence_adapter->GetTable(), $query->getFrom() ) ) {
142
+ return $occurence_adapter->LoadMulti( $sql, $args );
143
+ } else {
144
+ return $this->getActiveRecordAdapter()->LoadMulti( $sql, $args );
145
+ }
146
+ }
147
 
148
+ /**
149
+ * Count query
150
+ *
151
+ * @param object $query - Query object.
152
+ * @return integer counting records.
153
+ */
154
+ public function Count( $query ) {
155
+ // Back up columns, use COUNT as default column and generate sql.
156
+ $cols = $query->getColumns();
157
+ $query->clearColumns();
158
+ $query->addColumn( 'COUNT(*)' );
159
 
160
+ $args = array();
161
+ $sql = $this->GetSql( $query, $args );
162
 
163
+ // Restore columns.
164
+ $query->setColumns( $cols );
165
+ // Execute query and return result.
166
+ return $this->getActiveRecordAdapter()->CountQuery( $sql, $args );
167
+ }
168
 
169
+ /**
170
+ * Count DELETE query
171
+ *
172
+ * @param object $query - Query object.
173
+ * @return integer counting records.
174
+ */
175
+ public function CountDelete( $query ) {
176
+ $result = $this->GetSqlDelete( $query, true );
177
+ // Execute query and return result.
178
+ return $this->getActiveRecordAdapter()->CountQuery( $result['sql'], $result['args'] );
179
+ }
180
 
181
+ /**
182
+ * Query for deleting records
183
+ *
184
+ * @param object $query query object.
185
+ */
186
+ public function Delete( $query ) {
187
+ $result = $this->GetSqlDelete( $query );
188
+ $this->DeleteMetas( $query, $result['args'] );
189
+ return $this->getActiveRecordAdapter()->DeleteQuery( $result['sql'], $result['args'] );
190
+ }
191
 
192
+ /**
193
+ * Load occurrence IDs then delete Metadata by occurrence_id
194
+ *
195
+ * @param object $query - Query object.
196
+ * @param array $args - Args of the query.
197
+ */
198
+ public function DeleteMetas( $query, $args ) {
199
+ // Back up columns, use COUNT as default column and generate sql.
200
+ $cols = $query->getColumns();
201
+ $query->clearColumns();
202
+ $query->addColumn( 'id' );
203
+ $sql = $this->GetSql( $query );
204
+ // Restore columns.
205
+ $query->setColumns( $cols );
206
 
207
+ $_wpdb = $this->connection;
208
+ $occ_ids = array();
209
+ $sql = ( ! empty( $args ) ? $_wpdb->prepare( $sql, $args ) : $sql);
210
+ foreach ( $_wpdb->get_results( $sql, ARRAY_A ) as $data ) {
211
+ $occ_ids[] = $data['id'];
212
+ }
213
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
214
+ $meta->DeleteByOccurenceIds( $occ_ids );
215
+ }
216
 
217
+ /**
218
+ * Get the DELETE query SQL filled with the args.
219
+ *
220
+ * @param object $query - Query object.
221
+ * @param bool $get_count - Get count.
222
+ * @return string - Generated sql.
223
+ */
224
+ public function GetSqlDelete( $query, $get_count = false ) {
225
+ $result = array();
226
+ $args = array();
227
+ // Back up columns, remove them for DELETE and generate sql.
228
+ $cols = $query->getColumns();
229
+ $query->clearColumns();
230
 
231
+ $conditions = $query->getConditions();
232
 
233
+ $s_where_clause = '';
234
+ foreach ( $conditions as $field_name => $field_value ) {
235
+ if ( empty( $s_where_clause ) ) {
236
+ $s_where_clause .= ' WHERE ';
237
+ } else {
238
+ $s_where_clause .= ' AND ';
239
+ }
240
+ $s_where_clause .= $field_name;
241
+ $args[] = $field_value;
242
+ }
243
 
244
+ $from_data_sets = $query->getFrom();
245
+ $order_bys = $query->getOrderBy();
246
 
247
+ $s_limit_clause = '';
248
+ if ( $query->getLimit() ) {
249
+ $s_limit_clause .= ' LIMIT ';
250
+ if ( $query->getOffset() ) {
251
+ $s_limit_clause .= $query->getOffset() . ', ';
252
+ }
253
+ $s_limit_clause .= $query->getLimit();
254
+ }
255
+ $result['sql'] = ($get_count ? 'SELECT COUNT(*) FROM ' : 'DELETE FROM ')
256
+ . implode( ',', $from_data_sets )
257
+ . $s_where_clause
258
+ . ( ! empty( $order_bys ) ? (' ORDER BY ' . implode( ', ', array_keys( $order_bys ) ) . ' ' . implode( ', ', array_values( $order_bys ) )) : '')
259
+ . $s_limit_clause;
260
+ $result['args'] = $args;
261
+ // Restore columns.
262
+ $query->setColumns( $cols );
263
 
264
+ return $result;
265
+ }
266
 
267
+ /**
268
+ * Search by alert code OR by Metadata value.
269
+ *
270
+ * @param object $query - Query object.
271
+ */
272
+ public function SearchCondition( $query ) {
273
+ $condition = $query->getSearchCondition();
274
+ if ( empty( $condition ) ) {
275
+ return null;
276
+ }
277
+ $search_conditions = array();
278
+ $meta = new WSAL_Adapters_MySQL_Meta( $this->connection );
279
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $this->connection );
280
+ if ( is_numeric( $condition ) && strlen( $condition ) == 4 ) {
281
+ $search_conditions['sql'] = $occurrence->GetTable() . '.alert_id LIKE %s';
282
+ } else {
283
+ $search_conditions['sql'] = $occurrence->GetTable() . '.id IN (
284
+ SELECT DISTINCT occurrence_id
285
+ FROM ' . $meta->GetTable() . '
286
+ WHERE TRIM(BOTH "\"" FROM value) LIKE %s
287
+ )';
288
+ }
289
+ $search_conditions['args'] = '%' . $condition . '%';
290
+ return $search_conditions;
291
+ }
292
  }
classes/Adapters/MySQL/TmpUserAdapter.php CHANGED
@@ -1,45 +1,71 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * MySQL database TmpUser class.
5
  *
6
  * This class is used for create a temporary table to store the WP users
7
  * when the External DB Add-On is activated and the Alerts are stored on an external DB
8
  * because the query between plugin tables and the internal wp_uses table is not possible.
9
- * @see WSAL_Adapters_MySQL_ActiveRecord->GetReportGrouped()
 
 
10
  */
11
  class WSAL_Adapters_MySQL_TmpUser extends WSAL_Adapters_MySQL_ActiveRecord {
12
 
13
- protected $_table = 'wsal_tmp_users';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- /**
16
- * @return WSAL_Models_TmpUser
17
- */
18
- public function GetModel()
19
- {
20
- return new WSAL_Models_TmpUser();
21
- }
22
-
23
- public function __construct($conn)
24
- {
25
- parent::__construct($conn);
26
- }
27
-
28
- /**
29
- * @return string Must return SQL for creating table.
30
- */
31
- protected function _GetInstallQuery($prefix = false)
32
- {
33
- $_wpdb = $this->connection;
34
- $table_name = ($prefix) ? $this->GetWPTable() : $this->GetTable();
35
- $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
36
- $sql .= 'ID BIGINT NOT NULL,' . PHP_EOL;
37
- $sql .= 'user_login VARCHAR(60) NOT NULL,' . PHP_EOL;
38
- $sql .= 'INDEX (ID)' . PHP_EOL;
39
- $sql .= ')';
40
- if (!empty($_wpdb->charset)) {
41
- $sql .= ' DEFAULT CHARACTER SET ' . $_wpdb->charset;
42
- }
43
- return $sql;
44
- }
45
  }
1
  <?php
2
  /**
3
+ * Adapter: Meta data.
4
+ *
5
+ * MySQL database Metadata class.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * MySQL database TmpUser class.
17
  *
18
  * This class is used for create a temporary table to store the WP users
19
  * when the External DB Add-On is activated and the Alerts are stored on an external DB
20
  * because the query between plugin tables and the internal wp_uses table is not possible.
21
+ *
22
+ * @see WSAL_Adapters_MySQL_ActiveRecord->GetReportGrouped()
23
+ * @package Wsal
24
  */
25
  class WSAL_Adapters_MySQL_TmpUser extends WSAL_Adapters_MySQL_ActiveRecord {
26
 
27
+ /**
28
+ * Contains the table name.
29
+ *
30
+ * @var string
31
+ */
32
+ protected $_table = 'wsal_tmp_users';
33
+
34
+ /**
35
+ * Returns the model class for adapter.
36
+ *
37
+ * @return WSAL_Models_TmpUser
38
+ */
39
+ public function GetModel() {
40
+ return new WSAL_Models_TmpUser();
41
+ }
42
+
43
+ /**
44
+ * Method: Constructor.
45
+ *
46
+ * @param array $conn - Connection array.
47
+ */
48
+ public function __construct( $conn ) {
49
+ parent::__construct( $conn );
50
+ }
51
 
52
+ /**
53
+ * Must return SQL for creating table.
54
+ *
55
+ * @param mix $prefix - Prefix.
56
+ * @return string
57
+ */
58
+ protected function _GetInstallQuery( $prefix = false ) {
59
+ $_wpdb = $this->connection;
60
+ $table_name = ($prefix) ? $this->GetWPTable() : $this->GetTable();
61
+ $sql = 'CREATE TABLE IF NOT EXISTS ' . $table_name . ' (' . PHP_EOL;
62
+ $sql .= 'ID BIGINT NOT NULL,' . PHP_EOL;
63
+ $sql .= 'user_login VARCHAR(60) NOT NULL,' . PHP_EOL;
64
+ $sql .= 'INDEX (ID)' . PHP_EOL;
65
+ $sql .= ')';
66
+ if ( ! empty( $_wpdb->charset ) ) {
67
+ $sql .= ' DEFAULT CHARACTER SET ' . $_wpdb->charset;
68
+ }
69
+ return $sql;
70
+ }
 
 
 
 
 
 
 
 
 
 
 
71
  }
classes/Adapters/OccurrenceInterface.php CHANGED
@@ -1,15 +1,67 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * Interface used by the Occurrence.
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  */
7
- interface WSAL_Adapters_OccurrenceInterface
8
- {
9
- public function GetMeta($occurence);
10
- public function GetNamedMeta($occurence, $name);
11
- public function GetFirstNamedMeta($occurence, $names);
12
- public static function GetNewestUnique($limit = PHP_INT_MAX);
13
- public function CheckKnownUsers($args = array());
14
- public function CheckUnKnownUsers($args = array());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
1
  <?php
2
  /**
3
+ * Occurrence Interface.
4
  *
5
  * Interface used by the Occurrence.
6
+ *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Interface used by the Occurrence.
17
+ *
18
+ * @package Wsal
19
  */
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 $occurence - Instance of occurence object.
43
+ * @param array $names - List of Meta names.
44
+ */
45
+ public function GetFirstNamedMeta( $occurence, $names );
46
+
47
+ /**
48
+ * Returns newest unique occurrences.
49
+ *
50
+ * @param integer $limit - Maximum limit.
51
+ */
52
+ public static function GetNewestUnique( $limit = PHP_INT_MAX );
53
+
54
+ /**
55
+ * Gets occurences of the same type by IP and Username within specified time frame.
56
+ *
57
+ * @param array $args - Arguments.
58
+ */
59
+ public function CheckKnownUsers( $args = array() );
60
+
61
+ /**
62
+ * Gets occurences of the same type by IP within specified time frame.
63
+ *
64
+ * @param array $args - Arguments.
65
+ */
66
+ public function CheckUnKnownUsers( $args = array() );
67
  }
classes/Adapters/QueryInterface.php CHANGED
@@ -1,12 +1,42 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * Interface used by the Query.
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  */
7
- interface WSAL_Adapters_QueryInterface
8
- {
9
- public function Execute($query);
10
- public function Count($query);
11
- public function Delete($query);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  }
1
  <?php
2
  /**
3
+ * Query Interface.
4
  *
5
  * Interface used by the Query.
6
+ *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Interface used by the Query.
17
+ *
18
+ * @package Wsal
19
  */
20
+ interface WSAL_Adapters_QueryInterface {
21
+
22
+ /**
23
+ * Execute query and return data as $ar_cls objects.
24
+ *
25
+ * @param object $query - Query object.
26
+ */
27
+ public function Execute( $query );
28
+
29
+ /**
30
+ * Count query.
31
+ *
32
+ * @param object $query - Query object.
33
+ */
34
+ public function Count( $query );
35
+
36
+ /**
37
+ * Query for deleting records.
38
+ *
39
+ * @param object $query - Query object.
40
+ */
41
+ public function Delete( $query );
42
  }
classes/Alert.php CHANGED
@@ -1,119 +1,133 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
- *
5
  * WSAL_Alert Object class.
 
 
6
  */
7
- final class WSAL_Alert
8
- {
9
- /**
10
- * Alert type (used when triggering an alert etc).
11
- * @var integer
12
- */
13
- public $type = 0;
14
-
15
- /**
16
- * Alert error level (E_* constant).
17
- * @var integer
18
- */
19
- public $code = 0;
20
-
21
- /**
22
- * Alert category (alerts are grouped by matching categories).
23
- * @var string
24
- */
25
- public $catg = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- /**
28
- * Alert sub category.
29
- * @var string
30
- */
31
- public $subcatg = '';
32
-
33
- /**
34
- * Alert description (ie, describes what happens when alert is triggered).
35
- * @var string
36
- */
37
- public $desc = '';
38
-
39
- /**
40
- * Alert message (variables between '%' are expanded to values).
41
- * @var string
42
- */
43
- public $mesg = '';
44
-
45
- public function __construct($type = 0, $code = 0, $catg = '', $subcatg = '', $desc = '', $mesg = '')
46
- {
47
- $this->type = $type;
48
- $this->code = $code;
49
- $this->catg = $catg;
50
- $this->subcatg = $subcatg;
51
- $this->desc = $desc;
52
- $this->mesg = $mesg;
53
- }
54
-
55
- /**
56
- * Retrieves a value for a particular meta variable expression.
57
- * @param string $expr Expression, eg: User->Name looks for a Name property for meta named User.
58
- * @param array $metaData (Optional) Meta data relevant to expression.
59
- * @return mixed The value nearest to the expression.
60
- */
61
- protected function GetMetaExprValue($expr, $metaData = array())
62
- {
63
- // TODO Handle function calls (and methods?)
64
- $expr = explode('->', $expr);
65
- $meta = array_shift($expr);
66
- $meta = isset($metaData[$meta]) ? $metaData[$meta] : null;
67
- foreach ($expr as $part) {
68
- if (is_scalar($meta) || is_null($meta)) {
69
- return $meta; // this isn't 100% correct
70
- }
71
- $meta = is_array($meta) ? $meta[$part] : $meta->$part;
72
- }
73
- return is_scalar($meta) ? (string)$meta : var_export($meta, true);
74
- }
75
-
76
- /**
77
- * Expands a message with variables by replacing variables with meta data values.
78
- * @param string $mesg The original message.
79
- * @param array $metaData (Optional) Meta data relevant to message.
80
- * @param callable|null $metaFormatter (Optional) Callback for formatting meta values.
81
- * @param string $afterMeta (Optional) Some text to put after meta values.
82
- * @return string The expanded message.
83
- */
84
- protected function GetFormattedMesg($origMesg, $metaData = array(), $metaFormatter = null)
85
- {
86
- // tokenize message with regex
87
- $mesg = preg_split('/(%.*?%)/', (string)$origMesg, -1, PREG_SPLIT_DELIM_CAPTURE);
88
- if (!is_array($mesg)) {
89
- return (string)$origMesg;
90
- }
91
- // handle tokenized message
92
- foreach ($mesg as $i => $token) {
93
- // handle escaped percent sign
94
- if ($token == '%%') {
95
- $mesg[$i] = '%';
96
- } else if (substr($token, 0, 1) == '%' && substr($token, -1, 1) == '%') {
97
- // handle complex expressions
98
- $mesg[$i] = $this->GetMetaExprValue(substr($token, 1, -1), $metaData);
99
- if ($metaFormatter) {
100
- $mesg[$i] = call_user_func($metaFormatter, $token, $mesg[$i]);
101
- }
102
- }
103
- }
104
- // compact message and return
105
- return implode('', $mesg);
106
- }
107
-
108
- /**
109
- * Gets alert message.
110
- * @param array $metaData (Optional) Meta data relevant to message.
111
- * @param callable|null $metaFormatter (Optional) Meta formatter callback.
112
- * @param string|null $mesg (Optional) Override message template to use.
113
- * @return string Fully formatted message.
114
- */
115
- public function GetMessage($metaData = array(), $metaFormatter = null, $mesg = null)
116
- {
117
- return $this->GetFormattedMesg(is_null($mesg) ? $this->mesg : $mesg, $metaData, $metaFormatter);
118
- }
119
  }
1
  <?php
2
  /**
 
 
3
  * WSAL_Alert Object class.
4
+ *
5
+ * @package Wsal
6
  */
7
+ final class WSAL_Alert {
8
+
9
+ /**
10
+ * Alert type (used when triggering an alert etc).
11
+ *
12
+ * @var integer
13
+ */
14
+ public $type = 0;
15
+
16
+ /**
17
+ * Alert error level (E_* constant).
18
+ *
19
+ * @var integer
20
+ */
21
+ public $code = 0;
22
+
23
+ /**
24
+ * Alert category (alerts are grouped by matching categories).
25
+ *
26
+ * @var string
27
+ */
28
+ public $catg = '';
29
+
30
+ /**
31
+ * Alert sub category.
32
+ *
33
+ * @var string
34
+ */
35
+ public $subcatg = '';
36
+
37
+ /**
38
+ * Alert description (ie, describes what happens when alert is triggered).
39
+ *
40
+ * @var string
41
+ */
42
+ public $desc = '';
43
+
44
+ /**
45
+ * Alert message (variables between '%' are expanded to values).
46
+ *
47
+ * @var string
48
+ */
49
+ public $mesg = '';
50
+
51
+ /**
52
+ * Method: Constructor.
53
+ *
54
+ * @param integer $type - Type of alert.
55
+ * @param integer $code - Code of alert.
56
+ * @param string $catg - Category of alert.
57
+ * @param string $subcatg - Subcategory of alert.
58
+ * @param string $desc - Description.
59
+ * @param string $mesg - Alert message.
60
+ */
61
+ public function __construct( $type = 0, $code = 0, $catg = '', $subcatg = '', $desc = '', $mesg = '' ) {
62
+ $this->type = $type;
63
+ $this->code = $code;
64
+ $this->catg = $catg;
65
+ $this->subcatg = $subcatg;
66
+ $this->desc = $desc;
67
+ $this->mesg = $mesg;
68
+ }
69
+
70
+ /**
71
+ * Retrieves a value for a particular meta variable expression.
72
+ *
73
+ * @param string $expr Expression, eg: User->Name looks for a Name property for meta named User.
74
+ * @param array $meta_data (Optional) Meta data relevant to expression.
75
+ * @return mixed The value nearest to the expression.
76
+ */
77
+ protected function GetMetaExprValue( $expr, $meta_data = array() ) {
78
+ // TODO: Handle function calls (and methods?).
79
+ $expr = explode( '->', $expr );
80
+ $meta = array_shift( $expr );
81
+ $meta = isset( $meta_data[ $meta ] ) ? $meta_data[ $meta ] : null;
82
+ foreach ( $expr as $part ) {
83
+ if ( is_scalar( $meta ) || is_null( $meta ) ) {
84
+ return $meta; // This isn't 100% correct.
85
+ }
86
+ $meta = is_array( $meta ) ? $meta[ $part ] : $meta->$part;
87
+ }
88
+ return is_scalar( $meta ) ? (string) $meta : var_export( $meta, true );
89
+ }
90
+
91
+ /**
92
+ * Expands a message with variables by replacing variables with meta data values.
93
+ *
94
+ * @param string $orig_mesg The original message.
95
+ * @param array $meta_data (Optional) Meta data relevant to message.
96
+ * @param callable|null $meta_formatter (Optional) Callback for formatting meta values.
97
+ * @return string The expanded message.
98
+ */
99
+ protected function GetFormattedMesg( $orig_mesg, $meta_data = array(), $meta_formatter = null ) {
100
+ // Tokenize message with regex.
101
+ $mesg = preg_split( '/(%.*?%)/', (string) $orig_mesg, -1, PREG_SPLIT_DELIM_CAPTURE );
102
+ if ( ! is_array( $mesg ) ) {
103
+ return (string) $orig_mesg;
104
+ }
105
+ // Handle tokenized message.
106
+ foreach ( $mesg as $i => $token ) {
107
+ // Handle escaped percent sign.
108
+ if ( '%%' == $token ) {
109
+ $mesg[ $i ] = '%';
110
+ } elseif ( substr( $token, 0, 1 ) == '%' && substr( $token, -1, 1 ) == '%' ) {
111
+ // Handle complex expressions.
112
+ $mesg[ $i ] = $this->GetMetaExprValue( substr( $token, 1, -1 ), $meta_data );
113
+ if ( $meta_formatter ) {
114
+ $mesg[ $i ] = call_user_func( $meta_formatter, $token, $mesg[ $i ] );
115
+ }
116
+ }
117
+ }
118
+ // Compact message and return.
119
+ return implode( '', $mesg );
120
+ }
121
 
122
+ /**
123
+ * Gets alert message.
124
+ *
125
+ * @param array $meta_data (Optional) Meta data relevant to message.
126
+ * @param callable|null $meta_formatter (Optional) Meta formatter callback.
127
+ * @param string|null $mesg (Optional) Override message template to use.
128
+ * @return string Fully formatted message.
129
+ */
130
+ public function GetMessage( $meta_data = array(), $meta_formatter = null, $mesg = null ) {
131
+ return $this->GetFormattedMesg( is_null( $mesg ) ? $this->mesg : $mesg, $meta_data, $meta_formatter );
132
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  }
classes/AlertManager.php CHANGED
@@ -1,89 +1,97 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
- *
5
  * WSAL_AlertManager class.
6
  * It is the actual trigger for the alerts.
 
 
7
  */
8
- final class WSAL_AlertManager
9
- {
10
  /**
11
- * @var WSAL_Alert[]
 
 
12
  */
13
  protected $_alerts = array();
14
 
15
  /**
16
- * @var WSAL_AbstractLogger[]
 
 
17
  */
18
  protected $_loggers = array();
19
 
20
  /**
21
- * @var WpSecurityAuditLog
 
 
22
  */
23
  protected $plugin;
24
 
25
  /**
26
  * Contains a list of alerts to trigger.
 
27
  * @var array
28
  */
29
  protected $_pipeline = array();
30
 
31
  /**
32
  * Contains an array of alerts that have been triggered for this request.
 
33
  * @var int[]
34
  */
35
  protected $_triggered_types = array();
36
 
37
  /**
38
  * Create new AlertManager instance.
39
- * @param WpSecurityAuditLog $plugin
 
40
  */
41
- public function __construct(WpSecurityAuditLog $plugin)
42
- {
43
  $this->plugin = $plugin;
44
- foreach (glob(dirname(__FILE__) . '/Loggers/*.php') as $file) {
45
- $this->AddFromFile($file);
46
  }
47
 
48
- add_action('shutdown', array($this, '_CommitPipeline'));
49
  }
50
 
51
  /**
52
  * Add new logger from file inside autoloader path.
 
53
  * @param string $file Path to file.
54
  */
55
- public function AddFromFile($file)
56
- {
57
- $this->AddFromClass($this->plugin->GetClassFileClassName($file));
58
  }
59
 
60
  /**
61
  * Add new logger given class name.
 
62
  * @param string $class Class name.
63
  */
64
- public function AddFromClass($class)
65
- {
66
- $this->AddInstance(new $class($this->plugin));
67
  }
68
 
69
  /**
70
  * Add newly created logger to list.
 
71
  * @param WSAL_AbstractLogger $logger The new logger.
72
  */
73
- public function AddInstance(WSAL_AbstractLogger $logger)
74
- {
75
  $this->_loggers[] = $logger;
76
  }
77
 
78
  /**
79
  * Remove logger by class name.
 
80
  * @param string $class The class name.
81
  */
82
- public function RemoveByClass($class)
83
- {
84
- foreach ($this->_loggers as $i => $inst) {
85
- if (get_class($inst) == $class) {
86
- unset($this->_loggers[$i]);
87
  }
88
  }
89
  }
@@ -93,7 +101,7 @@ final class WSAL_AlertManager
93
  *
94
  * @param integer $type Alert type.
95
  * @param array $data Alert data.
96
- * @param bool $delayed - False if delayed, true if not.
97
  */
98
  public function Trigger( $type, $data = array(), $delayed = false ) {
99
 
@@ -134,17 +142,17 @@ final class WSAL_AlertManager
134
 
135
  /**
136
  * Check enable user and roles.
137
- * @param string user
138
- * @param array roles
139
- * @return boolean True if enable false otherwise.
 
140
  */
141
- public function CheckEnableUserRoles($user, $roles)
142
- {
143
  $is_enable = true;
144
- if ($user != "" && $this->IsDisabledUser($user)) {
145
  $is_enable = false;
146
  }
147
- if ($roles != "" && $this->IsDisabledRole($roles)) {
148
  $is_enable = false;
149
  }
150
  return $is_enable;
@@ -153,9 +161,9 @@ final class WSAL_AlertManager
153
  /**
154
  * Trigger only if a condition is met at the end of request.
155
  *
156
- * @param integer $type Alert type ID.
157
- * @param array $data Alert data.
158
- * @param callable $cond A future condition callback (receives an object of type WSAL_AlertManager as parameter).
159
  */
160
  public function TriggerIf( $type, $data, $cond = null ) {
161
  $username = wp_get_current_user()->user_login;
@@ -171,46 +179,55 @@ final class WSAL_AlertManager
171
  }
172
 
173
  /**
174
- * @internal Commit an alert now.
 
 
 
 
 
 
 
 
175
  */
176
- protected function _CommitItem($type, $data, $cond, $_retry = true)
177
- {
178
- if (!$cond || !!call_user_func($cond, $this)) {
179
- if ($this->IsEnabled($type)) {
180
- if (isset($this->_alerts[$type])) {
181
- // ok, convert alert to a log entry
182
  $this->_triggered_types[] = $type;
183
- $this->Log($type, $data);
184
- } elseif ($_retry) {
185
- // this is the last attempt at loading alerts from default file
186
  $this->plugin->LoadDefaults();
187
- return $this->_CommitItem($type, $data, $cond, false);
188
  } else {
189
- // in general this shouldn't happen, but it could, so we handle it here :)
190
- throw new Exception('Alert with code "' . $type . '" has not be registered.');
191
  }
192
  }
193
  }
194
  }
195
 
196
  /**
197
- * @internal Runs over triggered alerts in pipeline and passes them to loggers.
 
 
198
  */
199
- public function _CommitPipeline()
200
- {
201
- foreach ($this->_pipeline as $item) {
202
- $this->_CommitItem($item['type'], $item['data'], $item['cond']);
203
  }
204
  }
205
 
206
  /**
207
- * @param integer $type Alert type ID.
208
- * @return boolean True if at the end of request an alert of this type will be triggered.
 
 
209
  */
210
- public function WillTrigger($type)
211
- {
212
- foreach ($this->_pipeline as $item) {
213
- if ($item['type'] == $type) {
214
  return true;
215
  }
216
  }
@@ -218,48 +235,50 @@ final class WSAL_AlertManager
218
  }
219
 
220
  /**
221
- * @param int $type Alert type ID.
222
- * @return boolean True if an alert has been or will be triggered in this request, false otherwise.
 
 
223
  */
224
- public function WillOrHasTriggered($type)
225
- {
226
- return in_array($type, $this->_triggered_types)
227
- || $this->WillTrigger($type);
228
  }
229
 
230
  /**
231
  * Register an alert type.
232
- * @param array $info Array of [type, code, category, description, message] respectively.
 
 
233
  */
234
- public function Register($info)
235
- {
236
- if (func_num_args() == 1) {
237
- // handle single item
238
  list($type, $code, $catg, $subcatg, $desc, $mesg) = $info;
239
- if (isset($this->_alerts[$type])) {
240
- throw new Exception("Alert $type already registered with Alert Manager.");
241
  }
242
- $this->_alerts[$type] = new WSAL_Alert($type, $code, $catg, $subcatg, $desc, $mesg);
243
  } else {
244
- // handle multiple items
245
- foreach (func_get_args() as $arg) {
246
- $this->Register($arg);
247
  }
248
  }
249
  }
250
 
251
  /**
252
  * Register a whole group of items.
253
- * @param array $groups An array with group name as the index and an array of group items as the value.
 
254
  * Item values is an array of [type, code, description, message] respectively.
255
  */
256
- public function RegisterGroup($groups)
257
- {
258
- foreach ($groups as $name => $group) {
259
- foreach ($group as $subname => $subgroup) {
260
- foreach ($subgroup as $item) {
261
  list($type, $code, $desc, $mesg) = $item;
262
- $this->Register(array($type, $code, $name, $subname, $desc, $mesg));
263
  }
264
  }
265
  }
@@ -267,76 +286,78 @@ final class WSAL_AlertManager
267
 
268
  /**
269
  * Returns whether alert of type $type is enabled or not.
 
270
  * @param integer $type Alert type.
271
  * @return boolean True if enabled, false otherwise.
272
  */
273
- public function IsEnabled($type)
274
- {
275
- return !in_array($type, $this->GetDisabledAlerts());
276
  }
277
 
278
  /**
279
  * Disables a set of alerts by type.
 
280
  * @param int[] $types Alert type codes to be disabled.
281
  */
282
- public function SetDisabledAlerts($types)
283
- {
284
- $this->plugin->settings->SetDisabledAlerts($types);
285
  }
286
 
287
  /**
288
- * @return int[] Returns an array of disabled alerts' type code.
 
 
289
  */
290
- public function GetDisabledAlerts()
291
- {
292
  return $this->plugin->settings->GetDisabledAlerts();
293
  }
294
 
295
  /**
296
- * @return WSAL_AbstractLogger[] Returns an array of loaded loggers.
 
 
297
  */
298
- public function GetLoggers()
299
- {
300
  return $this->_loggers;
301
  }
302
 
303
  /**
304
  * Converts an Alert into a Log entry (by invoking loggers).
305
  * You should not call this method directly.
306
- * @param integer $type Alert type.
307
- * @param array $data Misc alert data.
308
- */
309
- protected function Log($type, $data = array())
310
- {
311
- if (!isset($data['ClientIP'])) {
312
- $clientIP = $this->plugin->settings->GetMainClientIP();
313
- if (!empty($clientIP)) {
314
- $data['ClientIP'] = $clientIP;
315
  }
316
  }
317
- if (!isset($data['OtherIPs']) && $this->plugin->settings->IsMainIPFromProxy()) {
318
- $otherIPs = $this->plugin->settings->GetClientIPs();
319
- if (!empty($otherIPs)) {
320
- $data['OtherIPs'] = $otherIPs;
321
  }
322
  }
323
- if (!isset($data['UserAgent'])) {
324
- if (isset($_SERVER['HTTP_USER_AGENT'])) {
325
  $data['UserAgent'] = $_SERVER['HTTP_USER_AGENT'];
326
  }
327
  }
328
- if (!isset($data['Username']) && !isset($data['CurrentUserID'])) {
329
- if (function_exists('get_current_user_id')) {
330
  $data['CurrentUserID'] = get_current_user_id();
331
  }
332
  }
333
- if (!isset($data['CurrentUserRoles']) && function_exists('is_user_logged_in') && is_user_logged_in()) {
334
- $currentUserRoles = $this->plugin->settings->GetCurrentUserRoles();
335
- if (!empty($currentUserRoles)) {
336
- $data['CurrentUserRoles'] = $currentUserRoles;
337
  }
338
  }
339
- // Check if the user management plugin is loaded and adds the SessionID
340
  if ( class_exists( 'WSAL_User_Management_Plugin' ) ) {
341
  if ( function_exists( 'get_current_user_id' ) ) {
342
  $session_tokens = get_user_meta( get_current_user_id(), 'session_tokens', true );
@@ -346,25 +367,22 @@ final class WSAL_AlertManager
346
  }
347
  }
348
  }
349
- //if(isset($_SERVER['REMOTE_HOST']) && $_SERVER['REMOTE_HOST'] != $data['ClientIP'])
350
- // $data['ClientHost'] = $_SERVER['REMOTE_HOST'];
351
- //$data['OtherIPs'] = $_SERVER['REMOTE_HOST'];
352
 
353
- foreach ($this->_loggers as $logger) {
354
- $logger->Log($type, $data);
355
  }
356
  }
357
 
358
  /**
359
  * Return alert given alert type.
360
- * @param integer $type Alert type.
361
- * @param mixed $default Returned if alert is not found.
 
362
  * @return WSAL_Alert
363
  */
364
- public function GetAlert($type, $default = null)
365
- {
366
- foreach ($this->_alerts as $alert) {
367
- if ($alert->type == $type) {
368
  return $alert;
369
  }
370
  }
@@ -373,30 +391,30 @@ final class WSAL_AlertManager
373
 
374
  /**
375
  * Returns all supported alerts.
 
376
  * @return WSAL_Alert[]
377
  */
378
- public function GetAlerts()
379
- {
380
  return $this->_alerts;
381
  }
382
 
383
  /**
384
  * Returns all supported alerts.
 
385
  * @return array
386
  */
387
- public function GetCategorizedAlerts()
388
- {
389
  $result = array();
390
- foreach ($this->_alerts as $alert) {
391
- if (!isset($result[$alert->catg])) {
392
- $result[$alert->catg] = array();
393
  }
394
- if (!isset($result[$alert->catg][$alert->subcatg])) {
395
- $result[$alert->catg][$alert->subcatg] = array();
396
  }
397
- $result[$alert->catg][$alert->subcatg][] = $alert;
398
  }
399
- ksort($result);
400
  return $result;
401
  }
402
 
@@ -411,7 +429,9 @@ final class WSAL_AlertManager
411
  }
412
 
413
  /**
414
- * @return Returns an array of disabled users.
 
 
415
  */
416
  public function GetDisabledUsers() {
417
  return $this->plugin->settings->GetExcludedMonitoringUsers();
@@ -463,6 +483,9 @@ final class WSAL_AlertManager
463
  return $this->plugin->settings->get_excluded_post_types();
464
  }
465
 
 
 
 
466
  private function IsDisabledIP() {
467
  $is_disabled = false;
468
  $ip = $this->plugin->settings->GetMainClientIP();
1
  <?php
2
  /**
 
 
3
  * WSAL_AlertManager class.
4
  * It is the actual trigger for the alerts.
5
+ *
6
+ * @package Wsal
7
  */
8
+ final class WSAL_AlertManager {
9
+
10
  /**
11
+ * Array of alerts (WSAL_Alert).
12
+ *
13
+ * @var array
14
  */
15
  protected $_alerts = array();
16
 
17
  /**
18
+ * Array of loggers (WSAL_AbstractLogger).
19
+ *
20
+ * @var array
21
  */
22
  protected $_loggers = array();
23
 
24
  /**
25
+ * Instance of WpSecurityAuditLog.
26
+ *
27
+ * @var object
28
  */
29
  protected $plugin;
30
 
31
  /**
32
  * Contains a list of alerts to trigger.
33
+ *
34
  * @var array
35
  */
36
  protected $_pipeline = array();
37
 
38
  /**
39
  * Contains an array of alerts that have been triggered for this request.
40
+ *
41
  * @var int[]
42
  */
43
  protected $_triggered_types = array();
44
 
45
  /**
46
  * Create new AlertManager instance.
47
+ *
48
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
49
  */
50
+ public function __construct( WpSecurityAuditLog $plugin ) {
 
51
  $this->plugin = $plugin;
52
+ foreach ( glob( dirname( __FILE__ ) . '/Loggers/*.php' ) as $file ) {
53
+ $this->AddFromFile( $file );
54
  }
55
 
56
+ add_action( 'shutdown', array( $this, '_CommitPipeline' ) );
57
  }
58
 
59
  /**
60
  * Add new logger from file inside autoloader path.
61
+ *
62
  * @param string $file Path to file.
63
  */
64
+ public function AddFromFile( $file ) {
65
+ $this->AddFromClass( $this->plugin->GetClassFileClassName( $file ) );
 
66
  }
67
 
68
  /**
69
  * Add new logger given class name.
70
+ *
71
  * @param string $class Class name.
72
  */
73
+ public function AddFromClass( $class ) {
74
+ $this->AddInstance( new $class( $this->plugin ) );
 
75
  }
76
 
77
  /**
78
  * Add newly created logger to list.
79
+ *
80
  * @param WSAL_AbstractLogger $logger The new logger.
81
  */
82
+ public function AddInstance( WSAL_AbstractLogger $logger ) {
 
83
  $this->_loggers[] = $logger;
84
  }
85
 
86
  /**
87
  * Remove logger by class name.
88
+ *
89
  * @param string $class The class name.
90
  */
91
+ public function RemoveByClass( $class ) {
92
+ foreach ( $this->_loggers as $i => $inst ) {
93
+ if ( get_class( $inst ) == $class ) {
94
+ unset( $this->_loggers[ $i ] );
 
95
  }
96
  }
97
  }
101
  *
102
  * @param integer $type Alert type.
103
  * @param array $data Alert data.
104
+ * @param bool $delayed - False if delayed, true if not.
105
  */
106
  public function Trigger( $type, $data = array(), $delayed = false ) {
107
 
142
 
143
  /**
144
  * Check enable user and roles.
145
+ *
146
+ * @param string $user - Username.
147
+ * @param array $roles - User roles.
148
+ * @return boolean - True if enable false otherwise.
149
  */
150
+ public function CheckEnableUserRoles( $user, $roles ) {
 
151
  $is_enable = true;
152
+ if ( '' != $user && $this->IsDisabledUser( $user ) ) {
153
  $is_enable = false;
154
  }
155
+ if ( '' != $roles && $this->IsDisabledRole( $roles ) ) {
156
  $is_enable = false;
157
  }
158
  return $is_enable;
161
  /**
162
  * Trigger only if a condition is met at the end of request.
163
  *
164
+ * @param integer $type - Alert type ID.
165
+ * @param array $data - Alert data.
166
+ * @param callable $cond - A future condition callback (receives an object of type WSAL_AlertManager as parameter).
167
  */
168
  public function TriggerIf( $type, $data, $cond = null ) {
169
  $username = wp_get_current_user()->user_login;
179
  }
180
 
181
  /**
182
+ * Method: Commit an alert now.
183
+ *
184
+ * @param int $type - Alert type.
185
+ * @param array $data - Data of the alert.
186
+ * @param array $cond - Condition for the alert.
187
+ * @param bool $_retry - Retry.
188
+ * @internal
189
+ *
190
+ * @throws string - Error if alert is not registered.
191
  */
192
+ protected function _CommitItem( $type, $data, $cond, $_retry = true ) {
193
+ if ( ! $cond || ! ! call_user_func( $cond, $this ) ) {
194
+ if ( $this->IsEnabled( $type ) ) {
195
+ if ( isset( $this->_alerts[ $type ] ) ) {
196
+ // Ok, convert alert to a log entry.
 
197
  $this->_triggered_types[] = $type;
198
+ $this->Log( $type, $data );
199
+ } elseif ( $_retry ) {
200
+ // This is the last attempt at loading alerts from default file.
201
  $this->plugin->LoadDefaults();
202
+ return $this->_CommitItem( $type, $data, $cond, false );
203
  } else {
204
+ // In general this shouldn't happen, but it could, so we handle it here.
205
+ throw new Exception( 'Alert with code "' . $type . '" has not be registered.' );
206
  }
207
  }
208
  }
209
  }
210
 
211
  /**
212
+ * Method: Runs over triggered alerts in pipeline and passes them to loggers.
213
+ *
214
+ * @internal
215
  */
216
+ public function _CommitPipeline() {
217
+ foreach ( $this->_pipeline as $item ) {
218
+ $this->_CommitItem( $item['type'], $item['data'], $item['cond'] );
 
219
  }
220
  }
221
 
222
  /**
223
+ * Method: True if at the end of request an alert of this type will be triggered.
224
+ *
225
+ * @param integer $type - Alert type ID.
226
+ * @return boolean
227
  */
228
+ public function WillTrigger( $type ) {
229
+ foreach ( $this->_pipeline as $item ) {
230
+ if ( $item['type'] == $type ) {
 
231
  return true;
232
  }
233
  }
235
  }
236
 
237
  /**
238
+ * Method: True if an alert has been or will be triggered in this request, false otherwise.
239
+ *
240
+ * @param int $type - Alert type ID.
241
+ * @return boolean
242
  */
243
+ public function WillOrHasTriggered( $type ) {
244
+ return in_array( $type, $this->_triggered_types )
245
+ || $this->WillTrigger( $type );
 
246
  }
247
 
248
  /**
249
  * Register an alert type.
250
+ *
251
+ * @param array $info - Array of [type, code, category, description, message] respectively.
252
+ * @throws string - Error if alert is already registered.
253
  */
254
+ public function Register( $info ) {
255
+ if ( func_num_args() == 1 ) {
256
+ // Handle single item.
 
257
  list($type, $code, $catg, $subcatg, $desc, $mesg) = $info;
258
+ if ( isset( $this->_alerts[ $type ] ) ) {
259
+ throw new Exception( "Alert $type already registered with Alert Manager." );
260
  }
261
+ $this->_alerts[ $type ] = new WSAL_Alert( $type, $code, $catg, $subcatg, $desc, $mesg );
262
  } else {
263
+ // Handle multiple items.
264
+ foreach ( func_get_args() as $arg ) {
265
+ $this->Register( $arg );
266
  }
267
  }
268
  }
269
 
270
  /**
271
  * Register a whole group of items.
272
+ *
273
+ * @param array $groups - An array with group name as the index and an array of group items as the value.
274
  * Item values is an array of [type, code, description, message] respectively.
275
  */
276
+ public function RegisterGroup( $groups ) {
277
+ foreach ( $groups as $name => $group ) {
278
+ foreach ( $group as $subname => $subgroup ) {
279
+ foreach ( $subgroup as $item ) {
 
280
  list($type, $code, $desc, $mesg) = $item;
281
+ $this->Register( array( $type, $code, $name, $subname, $desc, $mesg ) );
282
  }
283
  }
284
  }
286
 
287
  /**
288
  * Returns whether alert of type $type is enabled or not.
289
+ *
290
  * @param integer $type Alert type.
291
  * @return boolean True if enabled, false otherwise.
292
  */
293
+ public function IsEnabled( $type ) {
294
+ return ! in_array( $type, $this->GetDisabledAlerts() );
 
295
  }
296
 
297
  /**
298
  * Disables a set of alerts by type.
299
+ *
300
  * @param int[] $types Alert type codes to be disabled.
301
  */
302
+ public function SetDisabledAlerts( $types ) {
303
+ $this->plugin->settings->SetDisabledAlerts( $types );
 
304
  }
305
 
306
  /**
307
+ * Method: Returns an array of disabled alerts' type code.
308
+ *
309
+ * @return int[]
310
  */
311
+ public function GetDisabledAlerts() {
 
312
  return $this->plugin->settings->GetDisabledAlerts();
313
  }
314
 
315
  /**
316
+ * Method: Returns an array of loaded loggers.
317
+ *
318
+ * @return WSAL_AbstractLogger[]
319
  */
320
+ public function GetLoggers() {
 
321
  return $this->_loggers;
322
  }
323
 
324
  /**
325
  * Converts an Alert into a Log entry (by invoking loggers).
326
  * You should not call this method directly.
327
+ *
328
+ * @param integer $type - Alert type.
329
+ * @param array $data - Misc alert data.
330
+ */
331
+ protected function Log( $type, $data = array() ) {
332
+ if ( ! isset( $data['ClientIP'] ) ) {
333
+ $client_ip = $this->plugin->settings->GetMainClientIP();
334
+ if ( ! empty( $client_ip ) ) {
335
+ $data['ClientIP'] = $client_ip;
336
  }
337
  }
338
+ if ( ! isset( $data['OtherIPs'] ) && $this->plugin->settings->IsMainIPFromProxy() ) {
339
+ $other_ips = $this->plugin->settings->GetClientIPs();
340
+ if ( ! empty( $other_ips ) ) {
341
+ $data['OtherIPs'] = $other_ips;
342
  }
343
  }
344
+ if ( ! isset( $data['UserAgent'] ) ) {
345
+ if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
346
  $data['UserAgent'] = $_SERVER['HTTP_USER_AGENT'];
347
  }
348
  }
349
+ if ( ! isset( $data['Username'] ) && ! isset( $data['CurrentUserID'] ) ) {
350
+ if ( function_exists( 'get_current_user_id' ) ) {
351
  $data['CurrentUserID'] = get_current_user_id();
352
  }
353
  }
354
+ if ( ! isset( $data['CurrentUserRoles'] ) && function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) {
355
+ $current_user_roles = $this->plugin->settings->GetCurrentUserRoles();
356
+ if ( ! empty( $current_user_roles ) ) {
357
+ $data['CurrentUserRoles'] = $current_user_roles;
358
  }
359
  }
360
+ // Check if the user management plugin is loaded and adds the SessionID.
361
  if ( class_exists( 'WSAL_User_Management_Plugin' ) ) {
362
  if ( function_exists( 'get_current_user_id' ) ) {
363
  $session_tokens = get_user_meta( get_current_user_id(), 'session_tokens', true );
367
  }
368
  }
369
  }
 
 
 
370
 
371
+ foreach ( $this->_loggers as $logger ) {
372
+ $logger->Log( $type, $data );
373
  }
374
  }
375
 
376
  /**
377
  * Return alert given alert type.
378
+ *
379
+ * @param integer $type - Alert type.
380
+ * @param mixed $default - Returned if alert is not found.
381
  * @return WSAL_Alert
382
  */
383
+ public function GetAlert( $type, $default = null ) {
384
+ foreach ( $this->_alerts as $alert ) {
385
+ if ( $alert->type == $type ) {
 
386
  return $alert;
387
  }
388
  }
391
 
392
  /**
393
  * Returns all supported alerts.
394
+ *
395
  * @return WSAL_Alert[]
396
  */
397
+ public function GetAlerts() {
 
398
  return $this->_alerts;
399
  }
400
 
401
  /**
402
  * Returns all supported alerts.
403
+ *
404
  * @return array
405
  */
406
+ public function GetCategorizedAlerts() {
 
407
  $result = array();
408
+ foreach ( $this->_alerts as $alert ) {
409
+ if ( ! isset( $result[ $alert->catg ] ) ) {
410
+ $result[ $alert->catg ] = array();
411
  }
412
+ if ( ! isset( $result[ $alert->catg ][ $alert->subcatg ] ) ) {
413
+ $result[ $alert->catg ][ $alert->subcatg ] = array();
414
  }
415
+ $result[ $alert->catg ][ $alert->subcatg ][] = $alert;
416
  }
417
+ ksort( $result );
418
  return $result;
419
  }
420
 
429
  }
430
 
431
  /**
432
+ * Method: Returns an array of disabled users.
433
+ *
434
+ * @return array.
435
  */
436
  public function GetDisabledUsers() {
437
  return $this->plugin->settings->GetExcludedMonitoringUsers();
483
  return $this->plugin->settings->get_excluded_post_types();
484
  }
485
 
486
+ /**
487
+ * Method: Returns if IP is disabled or not.
488
+ */
489
  private function IsDisabledIP() {
490
  $is_disabled = false;
491
  $ip = $this->plugin->settings->GetMainClientIP();
classes/AuditLogListView.php CHANGED
@@ -1,575 +1,738 @@
1
  <?php
2
-
3
- require_once(ABSPATH . 'wp-admin/includes/admin.php');
4
- require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
5
  /**
6
- * @package Wsal
 
 
7
  *
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  * This view is included in Audit Log Viewer Page.
 
9
  * @see Views/AuditLog.php
 
10
  */
11
- class WSAL_AuditLogListView extends WP_List_Table
12
- {
13
- /**
14
- * @var WpSecurityAuditLog
15
- */
16
- protected $_plugin;
17
- protected $_gmt_offset_sec = 0;
18
-
19
- public function __construct($plugin)
20
- {
21
- $this->_plugin = $plugin;
22
-
23
- $timezone = $this->_plugin->settings->GetTimezone();
24
- if ($timezone) {
25
- $this->_gmt_offset_sec = get_option('gmt_offset') * HOUR_IN_SECONDS;
26
- } else {
27
- $this->_gmt_offset_sec = date('Z');
28
- }
29
-
30
- parent::__construct(array(
31
- 'singular' => 'log',
32
- 'plural' => 'logs',
33
- 'ajax' => true,
34
- 'screen' => 'interval-list',
35
- ));
36
- }
37
-
38
- public function no_items()
39
- {
40
- _e('No events so far.', 'wp-security-audit-log');
41
- }
42
-
43
- public function extra_tablenav($which)
44
- {
45
- // items-per-page widget
46
- $p = $this->_plugin->settings->GetViewPerPage();
47
- $items = array(5, 10, 15, 30, 50);
48
- if (!in_array($p, $items)) $items[] = $p;
49
-
50
- ?><div class="wsal-ipp wsal-ipp-<?php echo $which; ?>">
51
- <?php _e('Show ', 'wp-security-audit-log'); ?>
52
- <select class="wsal-ipps" onfocus="WsalIppsFocus(value);" onchange="WsalIppsChange(value);">
53
- <?php foreach ($items as $item) { ?>
54
- <option
55
- value="<?php echo is_string($item) ? '' : $item; ?>"
56
- <?php if ($item == $p) echo 'selected="selected"'; ?>><?php
57
- echo $item;
58
- ?></option>
59
- <?php } ?>
60
- </select>
61
- <?php _e(' Items', 'wp-security-audit-log'); ?>
62
- </div><?php
63
-
64
- // show site alerts widget
65
- if ($this->is_multisite() && $this->is_main_blog()) {
66
- $curr = $this->get_view_site_id();
67
- ?><div class="wsal-ssa wsal-ssa-<?php echo $which; ?>">
68
- <?php if ($this->get_site_count() > 15) { ?>
69
- <?php $curr = $curr ? get_blog_details($curr) : null; ?>
70
- <?php $curr = $curr ? ($curr->blogname . ' (' . $curr->domain . ')') : 'All Sites'; ?>
71
- <input type="text" class="wsal-ssas" value="<?php echo esc_attr($curr); ?>"/>
72
- <?php } else { ?>
73
- <select class="wsal-ssas" onchange="WsalSsasChange(value);">
74
- <option value="0"><?php _e('All Sites', 'wp-security-audit-log'); ?></option>
75
- <?php foreach ($this->get_sites() as $info) { ?>
76
- <option value="<?php echo $info->blog_id; ?>"
77
- <?php if ($info->blog_id == $curr) echo 'selected="selected"'; ?>><?php
78
- echo esc_html($info->blogname) . ' (' . esc_html($info->domain) . ')';
79
- ?></option>
80
- <?php } ?>
81
- </select>
82
- <?php } ?>
83
- </div><?php
84
- }
85
-
86
- // switch to live or archive DB
87
- if ($this->_plugin->settings->IsArchivingEnabled()) {
88
- $selected = 'live';
89
- $selected_db = get_transient('wsal_wp_selected_db');
90
- if ($selected_db && $selected_db == 'archive') {
91
- $selected = 'archive';
92
- }
93
- ?><div class="wsal-ssa wsal-db">
94
- <select class="wsal-db" onchange="WsalDBChange(value);">
95
- <option value="live" <?php if ($selected == 'live') echo 'selected="selected"'; ?>><?php _e('Live Database', 'wp-security-audit-log'); ?></option>
96
- <option value="archive" <?php if ($selected == 'archive') echo 'selected="selected"'; ?>><?php _e('Archive Database', 'wp-security-audit-log'); ?></option>
97
- </select>
98
- </div><?php
99
- }
100
- }
101
-
102
- /**
103
- * @param int|null $limit Maximum number of sites to return (null = no limit).
104
- * @return object Object with keys: blog_id, blogname, domain
105
- */
106
- public function get_sites($limit = null)
107
- {
108
- global $wpdb;
109
- // build query
110
- $sql = 'SELECT blog_id, domain FROM ' . $wpdb->blogs;
111
- if (!is_null($limit)) $sql .= ' LIMIT ' . $limit;
112
- // execute query
113
- $res = $wpdb->get_results($sql);
114
- // modify result
115
- foreach ($res as $row) {
116
- $row->blogname = get_blog_option($row->blog_id, 'blogname');
117
- }
118
- // return result
119
- return $res;
120
- }
121
-
122
- /**
123
- * @return int The number of sites on the network.
124
- */
125
- public function get_site_count()
126
- {
127
- global $wpdb;
128
- $sql = 'SELECT COUNT(*) FROM ' . $wpdb->blogs;
129
- return (int)$wpdb->get_var($sql);
130
- }
131
-
132
- public function get_columns() {
133
- $type_name = $this->_plugin->settings->get_type_username();
134
- if ( 'display_name' === $type_name ) {
135
- $name_column = __( 'User', 'wp-security-audit-log' );
136
- } elseif ( 'username' === $type_name ) {
137
- $name_column = __( 'Username', 'wp-security-audit-log' );
138
- }
139
- $cols = array(
140
- //'cb' => '<input type="checkbox" />',
141
- //'read' => __('Read', 'wp-security-audit-log'),
142
- 'type' => __( 'Alert ID', 'wp-security-audit-log' ),
143
- 'code' => __( 'Type', 'wp-security-audit-log' ),
144
- 'crtd' => __( 'Date', 'wp-security-audit-log' ),
145
- 'user' => $name_column,
146
- 'scip' => __( 'Source IP', 'wp-security-audit-log' ),
147
- );
148
- if ( $this->is_multisite() && $this->is_main_blog() && ! $this->is_specific_view() ) {
149
- $cols['site'] = __( 'Site', 'wp-security-audit-log' );
150
- }
151
- $cols['mesg'] = __( 'Message', 'wp-security-audit-log' );
152
- $sel_columns = $this->_plugin->settings->GetColumnsSelected();
153
- if ( ! empty( $sel_columns ) ) {
154
- unset( $cols );
155
- $sel_columns = (array) json_decode( $sel_columns );
156
- foreach ( $sel_columns as $key => $value ) {
157
- switch ( $key ) {
158
- case 'alert_code':
159
- $cols['type'] = __( 'Alert ID', 'wp-security-audit-log' );
160
- break;
161
- case 'type':
162
- $cols['code'] = __( 'Type', 'wp-security-audit-log' );
163
- break;
164
- case 'date':
165
- $cols['crtd'] = __( 'Date', 'wp-security-audit-log' );
166
- break;
167
- case 'username':
168
- $cols['user'] = $name_column;
169
- break;
170
- case 'source_ip':
171
- $cols['scip'] = __( 'Source IP', 'wp-security-audit-log' );
172
- break;
173
- case 'site':
174
- $cols['site'] = __( 'Site', 'wp-security-audit-log' );
175
- break;
176
- case 'message':
177
- $cols['mesg'] = __( 'Message', 'wp-security-audit-log' );
178
- break;
179
- }
180
- }
181
- }
182
- if ( $this->_plugin->settings->IsDataInspectorEnabled() ) {
183
- $cols['data'] = '';
184
- }
185
- return $cols;
186
- }
187
-
188
- public function column_cb($item)
189
- {
190
- return '<input type="checkbox" value="'.$item->id.'" '
191
- . 'name="'.esc_attr($this->_args['singular']).'[]"/>';
192
- }
193
-
194
- public function get_sortable_columns()
195
- {
196
- return array(
197
- 'read' => array('is_read', false),
198
- //'code' => array('code', false),
199
- 'type' => array('alert_id', false),
200
- 'crtd' => array('created_on', true),
201
- 'user' => array('user', true),
202
- 'scip' => array('scip', false)
203
- );
204
- }
205
-
206
- public function column_default($item, $column_name)
207
- {
208
- //example: $item->getMetaValue('CurrentUserID')
209
- $datetimeFormat = $this->_plugin->settings->GetDatetimeFormat();
210
-
211
- switch ($column_name) {
212
- case 'read':
213
- return '<span class="log-read log-read-'
214
- . ($item->is_read ? 'old' : 'new')
215
- . '" title="' . __('Click to toggle.', 'wp-security-audit-log') . '"></span>';
216
- case 'type':
217
- $code = $this->_plugin->alerts->GetAlert( $item->alert_id );
218
- $extra_msg = '';
219
- $data_link = '';
220
- $modification_alerts = array( 1002, 1003, 6007, 6023 );
221
- if ( in_array( $item->alert_id, $modification_alerts, true ) ) {
222
- $extra_msg = '. Modify this alert.';
223
- if ( 1002 === $item->alert_id || 1003 === $item->alert_id ) {
224
- $data_link = add_query_arg( 'page', 'wsal-togglealerts#tab-users-profiles---activity', admin_url( 'admin.php' ) );
225
- } elseif ( 6007 === $item->alert_id || 6023 === $item->alert_id ) {
226
- $data_link = add_query_arg( 'page', 'wsal-togglealerts#tab-system-activity', admin_url( 'admin.php' ) );
227
- }
228
- }
229
- return '<span class="log-disable" data-tooltip="' . __( 'Disable this type of alerts.', 'wp-security-audit-log' ) . '<br>' . $item->alert_id . ' - ' . esc_html( $code->desc ) . $extra_msg . '" data-alert-id="' . $item->alert_id . '" ' . esc_attr( 'data-link=' . $data_link ) . ' >'
230
- . str_pad($item->alert_id, 4, '0', STR_PAD_LEFT) . ' </span>';
231
- case 'code':
232
- $code = $this->_plugin->alerts->GetAlert($item->alert_id);
233
- $code = $code ? $code->code : 0;
234
- $const = (object)array('name' => 'E_UNKNOWN', 'value' => 0, 'description' => __('Unknown error code.', 'wp-security-audit-log'));
235
- $const = $this->_plugin->constants->GetConstantBy('value', $code, $const);
236
- if ($const->name == 'E_CRITICAL') {
237
- $const->name = 'Critical';
238
- } else if ($const->name == 'E_WARNING') {
239
- $const->name = 'Warning';
240
- } else if ($const->name == 'E_NOTICE') {
241
- $const->name = 'Notification';
242
- }
243
- return '<a class="tooltip" href="#" data-tooltip="'. esc_html($const->name) .'"><span class="log-type log-type-'. $const->value
244
- .'"></span></a>';
245
- case 'crtd':
246
- return $item->created_on ? (
247
- str_replace(
248
- '$$$',
249
- substr(number_format(fmod($item->created_on + $this->_gmt_offset_sec, 1), 3), 2),
250
- date($datetimeFormat, $item->created_on + $this->_gmt_offset_sec)
251
- )
252
- ) : '<i>unknown</i>';
253
- case 'user':
254
- $username = $item->GetUsername();
255
- $type_name = $this->_plugin->settings->get_type_username();
256
- if ( $username && ( $user = get_user_by( 'login', $username ) ) ) {
257
- $image = get_avatar( $user->ID, 32 );
258
- if ( 'display_name' == $type_name ) {
259
- $display_name = $user->first_name . ' ' . $user->last_name;
260
- } elseif ( 'username' == $type_name ) {
261
- $display_name = $user->user_login;
262
- }
263
-
264
- if ( class_exists( 'WSAL_SearchExtension' ) ) {
265
- $tooltip = esc_attr__( 'Show me all activity by this User', 'wp-security-audit-log' );
266
-
267
- $uhtml = '<a class="search-user" data-tooltip="' . $tooltip . '" data-user="' . $user->user_login . '" href="' . admin_url( 'user-edit.php?user_id=' . $user->ID )
268
- . '" target="_blank">' . esc_html( $display_name ) . '</a>';
269
- } else {
270
- $uhtml = '<a href="' . admin_url( 'user-edit.php?user_id=' . $user->ID )
271
- . '" target="_blank">' . esc_html( $display_name ) . '</a>';
272
- }
273
-
274
- $roles = $item->GetUserRoles();
275
- if ( is_array( $roles ) && count( $roles ) ) {
276
- $roles = esc_html( ucwords( implode( ', ', $roles ) ) );
277
- } elseif ( is_string( $roles ) && $roles != '' ) {
278
- $roles = esc_html( ucwords( str_replace( array( "\"", "[", "]" ), " ", $roles ) ) );
279
- } else {
280
- $roles = '<i>' . __( 'Unknown', 'wp-security-audit-log' ) . '</i>';
281
- }
282
- } elseif ( 'Plugin' == $username ) {
283
- $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/plugin-logo.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
284
- $uhtml = '<i>' . __( 'Plugin', 'wp-security-audit-log' ) . '</i>';
285
- $roles = '';
286
- } elseif ( 'Plugins' == $username ) {
287
- $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/wordpress-logo-32.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
288
- $uhtml = '<i>' . __( 'Plugins', 'wp-security-audit-log' ) . '</i>';
289
- $roles = '';
290
- } elseif ( 'Website Visitor' == $username ) {
291
- $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/wordpress-logo-32.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
292
- $uhtml = '<i>' . __( 'Website Visitor', 'wp-security-audit-log' ) . '</i>';
293
- $roles = '';
294
- } else {
295
- $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/wordpress-logo-32.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
296
- $uhtml = '<i>' . __( 'System', 'wp-security-audit-log' ) . '</i>';
297
- $roles = '';
298
- }
299
- return $image . $uhtml . '<br/>' . $roles;
300
- case 'scip':
301
- $scip = $item->GetSourceIP();
302
- if ( is_string( $scip ) ) {
303
- $scip = str_replace( array( "\"", "[", "]" ), '', $scip );
304
- }
305
-
306
- $oips = array(); // $item->GetOtherIPs();
307
-
308
- // if there's no IP...
309
- if ( is_null( $scip ) || '' == $scip ) {
310
- return '<i>unknown</i>';
311
- }
312
-
313
- // if there's only one IP...
314
- $link = 'http://whatismyipaddress.com/ip/' . $scip . '?utm_source=plugin&utm_medium=referral&utm_campaign=WPSAL';
315
- if ( class_exists( 'WSAL_SearchExtension' ) ) {
316
- $tooltip = esc_attr__( 'Show me all activity originating from this IP Address', 'wp-security-audit-log' );
317
-
318
- if ( count( $oips ) < 2 ) {
319
- return "<a class='search-ip' data-tooltip='$tooltip' data-ip='$scip' target='_blank' href='$link'>" . esc_html( $scip ) . '</a>';
320
- }
321
- } else {
322
- if ( count( $oips ) < 2 ) {
323
- return "<a target='_blank' href='$link'>" . esc_html( $scip ) . '</a>';
324
- }
325
- }
326
-
327
- // if there are many IPs...
328
- if ( class_exists( 'WSAL_SearchExtension' ) ) {
329
- $tooltip = esc_attr__( 'Show me all activity originating from this IP Address', 'wp-security-audit-log' );
330
-
331
- $html = "<a class='search-ip' data-tooltip='$tooltip' data-ip='$scip' target='_blank' href='http://whatismyipaddress.com/ip/$scip'>" . esc_html( $scip ) . '</a> <a href="javascript:;" onclick="jQuery(this).hide().next().show();">(more&hellip;)</a><div style="display: none;">';
332
- foreach ( $oips as $ip ) if ( $scip != $ip ) $html .= '<div>' . $ip . '</div>';
333
- $html .= '</div>';
334
- return $html;
335
- } else {
336
- $html = "<a target='_blank' href='http://whatismyipaddress.com/ip/$scip'>" . esc_html( $scip ) . '</a> <a href="javascript:;" onclick="jQuery(this).hide().next().show();">(more&hellip;)</a><div style="display: none;">';
337
- foreach ( $oips as $ip ) if ( $scip != $ip ) $html .= '<div>' . $ip . '</div>';
338
- $html .= '</div>';
339
- return $html;
340
- }
341
-
342
- case 'site':
343
- $info = get_blog_details($item->site_id, true);
344
- return !$info ? ('Unknown Site '.$item->site_id)
345
- : ('<a href="' . esc_attr($info->siteurl) . '">' . esc_html($info->blogname) . '</a>');
346
- case 'mesg':
347
- return '<div id="Event' . $item->id . '">' . $item->GetMessage(array($this, 'meta_formatter')) . '</div>';
348
- case 'data':
349
- $url = admin_url('admin-ajax.php') . '?action=AjaxInspector&amp;occurrence=' . $item->id;
350
- return '<a class="more-info thickbox" title="' . __('Alert Data Inspector', 'wp-security-audit-log') . '"'
351
- . ' href="' . $url . '&amp;TB_iframe=true&amp;width=600&amp;height=550">&hellip;</a>';
352
- default:
353
- return isset($item->$column_name)
354
- ? esc_html($item->$column_name)
355
- : 'Column "' . esc_html($column_name) . '" not found';
356
- }
357
- }
358
-
359
- public function reorder_items_str($a, $b)
360
- {
361
- $result = strcmp($a->{$this->_orderby}, $b->{$this->_orderby});
362
- return ($this->_order === 'asc') ? $result : -$result;
363
- }
364
-
365
- public function reorder_items_int($a, $b)
366
- {
367
- $result = $a->{$this->_orderby} - $b->{$this->_orderby};
368
- return ($this->_order === 'asc') ? $result : -$result;
369
- }
370
-
371
- public function meta_formatter($name, $value)
372
- {
373
- switch (true) {
374
- case $name == '%Message%':
375
- return esc_html($value);
376
-
377
- case $name == '%PromoMessage%':
378
- return '<p class="promo-alert">' . $value .'</p>';
379
-
380
- case $name == '%PromoLink%':
381
- case $name == '%CommentLink%':
382
- case $name == '%CommentMsg%':
383
- return $value;
384
-
385
- case $name == '%MetaLink%':
386
- if (!empty($value)) {
387
- return "<a href=\"#\" onclick=\"WsalDisableCustom(this, '".$value."');\"> Exclude Custom Field from the Monitoring</a>";
388
- } else {
389
- return "";
390
- }
391
-
392
- case $name == '%RevisionLink%':
393
- return ' Click <a target="_blank" href="'.esc_url($value).'">here</a> to see the content changes.';
394
-
395
- case $name == '%EditorLinkPost%':
396
- return ' <a target="_blank" href="'.esc_url($value).'">View the post</a>';
397
-
398
- case $name == '%EditorLinkPage%':
399
- return ' <a target="_blank" href="'.esc_url($value).'">View the page</a>';
400
-
401
- case $name == '%CategoryLink%':
402
- return ' <a target="_blank" href="'.esc_url($value).'">View the category</a>';
403
-
404
- case $name == '%TagLink%':
405
- return ' <a target="_blank" href="'.esc_url($value).'">View the tag</a>';
406
-
407
- case $name == '%EditorLinkForum%':
408
- return ' <a target="_blank" href="'.esc_url($value).'">View the forum</a>';
409
-
410
- case $name == '%EditorLinkTopic%':
411
- return ' <a target="_blank" href="'.esc_url($value).'">View the topic</a>';
412
-
413
- case in_array($name, array('%MetaValue%', '%MetaValueOld%', '%MetaValueNew%')):
414
- return '<strong>' . (
415
- strlen($value) > 50 ? (esc_html(substr($value, 0, 50)) . '&hellip;') : esc_html($value)
416
- ) . '</strong>';
417
-
418
- case $name == '%ClientIP%':
419
- if (is_string($value)) {
420
- return '<strong>' . str_replace(array("\"", "[", "]"), "", $value) . '</strong>';
421
- } else {
422
- return '<i>unknown</i>';
423
- }
424
-
425
- case $name == '%LinkFile%':
426
- if ($value != 'NULL') {
427
- return '<a href="'.esc_url($value).'" download>Download the Log file</a>';
428
- } else {
429
- return 'Click <a href="'.esc_url(admin_url("admin.php?page=wsal-togglealerts#tab-system-activity")).'">here</a> to log such requests to file';
430
- }
431
-
432
- case '%LogFileLink%' === $name:
433
- if ( ! empty( $value ) && 'on' === $this->_plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
434
- return '<a href="' . esc_url( $value ) . '" download>Download the Log file</a>';
435
- } elseif ( ! empty( $value ) ) {
436
- return '<a href="' . esc_url( $value ) . '">Keep a record of the usernames</a>';
437
- }
438
- // Failed login file link.
439
- case '%LogFileText%' === $name:
440
- return esc_html( $value );
441
- // Failed login file text.
442
- case strncmp($value, 'http://', 7) === 0:
443
- case strncmp($value, 'https://', 7) === 0:
444
- return '<a href="' . esc_html($value) . '"' . ' title="' . esc_html($value) . '"' . ' target="_blank">' . esc_html($value) . '</a>';
445
-
446
- default:
447
- return '<strong>' . esc_html($value) . '</strong>';
448
- }
449
- }
450
-
451
- protected function is_multisite()
452
- {
453
- return $this->_plugin->IsMultisite();
454
- }
455
-
456
- protected function is_main_blog()
457
- {
458
- return get_current_blog_id() == 1;
459
- }
460
-
461
- protected function is_specific_view()
462
- {
463
- return isset($_REQUEST['wsal-cbid']) && $_REQUEST['wsal-cbid'] != '0';
464
- }
465
-
466
- protected function get_specific_view()
467
- {
468
- return isset($_REQUEST['wsal-cbid']) ? (int)$_REQUEST['wsal-cbid'] : 0;
469
- }
470
-
471
- protected function get_view_site_id()
472
- {
473
- switch (true) {
474
- // non-multisite
475
- case !$this->is_multisite():
476
- return 0;
477
- // multisite + main site view
478
- case $this->is_main_blog() && !$this->is_specific_view():
479
- return 0;
480
- // multisite + switched site view
481
- case $this->is_main_blog() && $this->is_specific_view():
482
- return $this->get_specific_view();
483
- // multisite + local site view
484
- default:
485
- return get_current_blog_id();
486
- }
487
- }
488
-
489
- public function prepare_items()
490
- {
491
- if ($this->_plugin->settings->IsArchivingEnabled()) {
492
- // Switch to Archive DB
493
- $selected_db = get_transient('wsal_wp_selected_db');
494
- if ($selected_db && $selected_db == 'archive') {
495
- $this->_plugin->settings->SwitchToArchiveDB();
496
- }
497
- }
498
-
499
- $per_page = $this->_plugin->settings->GetViewPerPage();
500
-
501
- $columns = $this->get_columns();
502
- $hidden = array();
503
- $sortable = $this->get_sortable_columns();
504
-
505
- $this->_column_headers = array($columns, $hidden, $sortable);
506
-
507
- //$this->process_bulk_action();
508
- //TO DO: Get rid of OccurrenceQuery and use the Occurence Model
509
- $query = new WSAL_Models_OccurrenceQuery();
510
-
511
- $bid = (int)$this->get_view_site_id();
512
- if ($bid) {
513
- $query->addCondition("site_id = %s ", $bid);
514
- }
515
-
516
- $query = apply_filters('wsal_auditlog_query', $query);
517
-
518
- $total_items = $query->getAdapter()->Count($query);
519
-
520
- if (empty($_REQUEST["orderby"])) {
521
- $query->addOrderBy("created_on", true);
522
- } else {
523
- $orderByField = $_REQUEST["orderby"];
524
-
525
- $isDescending = true;
526
- if (!empty($_REQUEST['order']) && $_REQUEST["order"] == "asc") {
527
- $isDescending = false;
528
- }
529
-
530
- //TO DO: Allow order by meta values
531
- if ($orderByField == "scip") {
532
- $query->addMetaJoin();
533
- $query->addOrderBy('CASE WHEN meta.name = "ClientIP" THEN meta.value END', $isDescending);
534
- } else if ($orderByField == "user") {
535
- $query->addMetaJoin();
536
- $query->addOrderBy('CASE WHEN meta.name = "CurrentUserID" THEN meta.value END', $isDescending);
537
- } else {
538
- $tmp = new WSAL_Models_Occurrence();
539
- //Making sure the field exists to order by
540
- if (isset($tmp->{$orderByField})) {
541
- // TODO we used to use a custom comparator ... is it safe to let MySQL do the ordering now?
542
- $query->addOrderBy($_REQUEST["orderby"], $isDescending);
543
-
544
- } else {
545
- $query->addOrderBy("created_on", true);
546
- }
547
- }
548
- }
549
-
550
- /** @todo Modify $query instead */
551
- /** @deprecated */
552
- //$data = array_slice($data, ($this->get_pagenum() - 1) * $per_page, $per_page);
553
- $query->setOffset(($this->get_pagenum() - 1) * $per_page);
554
- $query->setLimit($per_page);
555
-
556
- $this->items = $query->getAdapter()->Execute($query);
557
-
558
- $this->set_pagination_args(array(
559
- 'total_items' => $total_items,
560
- 'per_page' => $per_page,
561
- 'total_pages' => ceil($total_items / $per_page)
562
- ));
563
- }
564
-
565
- public function single_row($item)
566
- {
567
- if ($item->alert_id == 9999) {
568
- echo '<tr style="background-color: #D5E46E">';
569
- $this->single_row_columns($item);
570
- echo '</tr>';
571
- } else {
572
- parent::single_row($item);
573
- }
574
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575
  }
1
  <?php
 
 
 
2
  /**
3
+ * Audit Log List View
4
+ *
5
+ * CLass file for audit log list view.
6
  *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ require_once( ABSPATH . 'wp-admin/includes/admin.php' );
17
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
18
+
19
+ /**
20
  * This view is included in Audit Log Viewer Page.
21
+ *
22
  * @see Views/AuditLog.php
23
+ * @package Wsal
24
  */
25
+ class WSAL_AuditLogListView extends WP_List_Table {
26
+
27
+ /**
28
+ * Instance of WpSecurityAuditLog.
29
+ *
30
+ * @var object
31
+ */
32
+ protected $_plugin;
33
+
34
+ /**
35
+ * GMT Offset
36
+ *
37
+ * @var string
38
+ */
39
+ protected $_gmt_offset_sec = 0;
40
+
41
+ /**
42
+ * Method: Constructor.
43
+ *
44
+ * @param object $plugin - Instance of WpSecurityAuditLog.
45
+ */
46
+ public function __construct( $plugin ) {
47
+ $this->_plugin = $plugin;
48
+
49
+ $timezone = $this->_plugin->settings->GetTimezone();
50
+ if ( $timezone ) {
51
+ $this->_gmt_offset_sec = get_option( 'gmt_offset' ) * HOUR_IN_SECONDS;
52
+ } else {
53
+ $this->_gmt_offset_sec = date( 'Z' );
54
+ }
55
+
56
+ parent::__construct(
57
+ array(
58
+ 'singular' => 'log',
59
+ 'plural' => 'logs',
60
+ 'ajax' => true,
61
+ 'screen' => 'interval-list',
62
+ )
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Empty View.
68
+ */
69
+ public function no_items() {
70
+ esc_html_e( 'No events so far.', 'wp-security-audit-log' );
71
+ }
72
+
73
+ /**
74
+ * Table navigation.
75
+ *
76
+ * @param string $which - Position of the nav.
77
+ */
78
+ public function extra_tablenav( $which ) {
79
+ // Items-per-page widget.
80
+ $p = $this->_plugin->settings->GetViewPerPage();
81
+ $items = array( 5, 10, 15, 30, 50 );
82
+ if ( ! in_array( $p, $items ) ) {
83
+ $items[] = $p;
84
+ }
85
+
86
+ ?>
87
+ <div class="wsal-ipp wsal-ipp-<?php echo esc_attr( $which ); ?>">
88
+ <?php esc_html_e( 'Show ', 'wp-security-audit-log' ); ?>
89
+ <select class="wsal-ipps" onfocus="WsalIppsFocus(value);" onchange="WsalIppsChange(value);">
90
+ <?php foreach ( $items as $item ) { ?>
91
+ <option
92
+ value="<?php echo is_string( $item ) ? '' : esc_attr( $item ); ?>"
93
+ <?php
94
+ if ( $item == $p ) {
95
+ echo 'selected="selected"';
96
+ }
97
+ ?>
98
+ >
99
+ <?php echo esc_html( $item ); ?>
100
+ </option>
101
+ <?php } ?>
102
+ </select>
103
+ <?php esc_html_e( ' Items', 'wp-security-audit-log' ); ?>
104
+ </div>
105
+
106
+ <?php
107
+ // Show site alerts widget.
108
+ if ( $this->is_multisite() && $this->is_main_blog() ) {
109
+ $curr = $this->get_view_site_id();
110
+ ?>
111
+ <div class="wsal-ssa wsal-ssa-<?php echo esc_attr( $which ); ?>">
112
+ <?php if ( $this->get_site_count() > 15 ) { ?>
113
+ <?php $curr = $curr ? get_blog_details( $curr ) : null; ?>
114
+ <?php $curr = $curr ? ($curr->blogname . ' (' . $curr->domain . ')') : 'All Sites'; ?>
115
+ <input type="text" class="wsal-ssas" value="<?php echo esc_attr( $curr ); ?>"/>
116
+ <?php } else { ?>
117
+ <select class="wsal-ssas" onchange="WsalSsasChange(value);">
118
+ <option value="0"><?php esc_html_e( 'All Sites', 'wp-security-audit-log' ); ?></option>
119
+ <?php foreach ( $this->get_sites() as $info ) { ?>
120
+ <option value="<?php echo esc_attr( $info->blog_id ); ?>"
121
+ <?php
122
+ if ( $info->blog_id == $curr ) {
123
+ echo 'selected="selected"';
124
+ }
125
+ ?>
126
+ >
127
+ <?php echo esc_html( $info->blogname ) . ' (' . esc_html( $info->domain ) . ')'; ?>
128
+ </option>
129
+ <?php } ?>
130
+ </select>
131
+ <?php } ?>
132
+ </div>
133
+ <?php
134
+ }
135
+
136
+ // Switch to live or archive DB.
137
+ if ( $this->_plugin->settings->IsArchivingEnabled() ) {
138
+ $selected = 'live';
139
+ $selected_db = get_transient( 'wsal_wp_selected_db' );
140
+ if ( $selected_db && 'archive' == $selected_db ) {
141
+ $selected = 'archive';
142
+ }
143
+ ?>
144
+ <div class="wsal-ssa wsal-db">
145
+ <select class="wsal-db" onchange="WsalDBChange(value);">
146
+ <option value="live"
147
+ <?php
148
+ if ( 'live' == $selected ) {
149
+ echo 'selected="selected"';
150
+ }
151
+ ?>
152
+ >
153
+ <?php esc_html_e( 'Live Database', 'wp-security-audit-log' ); ?>
154
+ </option>
155
+ <option value="archive"
156
+ <?php
157
+ if ( 'archive' == $selected ) {
158
+ echo 'selected="selected"';
159
+ }
160
+ ?>
161
+ >
162
+ <?php esc_html_e( 'Archive Database', 'wp-security-audit-log' ); ?>
163
+ </option>
164
+ </select>
165
+ </div>
166
+ <?php
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Method: Object with keys: blog_id, blogname, domain.
172
+ *
173
+ * @param int|null $limit - Maximum number of sites to return (null = no limit).
174
+ * @return object
175
+ */
176
+ public function get_sites( $limit = null ) {
177
+ global $wpdb;
178
+ // Build query.
179
+ $sql = 'SELECT blog_id, domain FROM ' . $wpdb->blogs;
180
+ if ( ! is_null( $limit ) ) {
181
+ $sql .= ' LIMIT ' . $limit;
182
+ }
183
+
184
+ // Execute query.
185
+ $res = $wpdb->get_results( $sql );
186
+
187
+ // Modify result.
188
+ foreach ( $res as $row ) {
189
+ $row->blogname = get_blog_option( $row->blog_id, 'blogname' );
190
+ }
191
+
192
+ // Return result.
193
+ return $res;
194
+ }
195
+
196
+ /**
197
+ * Method: The number of sites on the network.
198
+ *
199
+ * @return int
200
+ */
201
+ public function get_site_count() {
202
+ global $wpdb;
203
+ $sql = 'SELECT COUNT(*) FROM ' . $wpdb->blogs;
204
+ return (int) $wpdb->get_var( $sql );
205
+ }
206
+
207
+ /**
208
+ * Method: Get View Columns.
209
+ *
210
+ * @return array
211
+ */
212
+ public function get_columns() {
213
+ $type_name = $this->_plugin->settings->get_type_username();
214
+ if ( 'display_name' === $type_name ) {
215
+ $name_column = __( 'User', 'wp-security-audit-log' );
216
+ } elseif ( 'username' === $type_name ) {
217
+ $name_column = __( 'Username', 'wp-security-audit-log' );
218
+ }
219
+ $cols = array(
220
+ 'type' => __( 'Alert ID', 'wp-security-audit-log' ),
221
+ 'code' => __( 'Type', 'wp-security-audit-log' ),
222
+ 'crtd' => __( 'Date', 'wp-security-audit-log' ),
223
+ 'user' => $name_column,
224
+ 'scip' => __( 'Source IP', 'wp-security-audit-log' ),
225
+ );
226
+ if ( $this->is_multisite() && $this->is_main_blog() && ! $this->is_specific_view() ) {
227
+ $cols['site'] = __( 'Site', 'wp-security-audit-log' );
228
+ }
229
+ $cols['mesg'] = __( 'Message', 'wp-security-audit-log' );
230
+ $sel_columns = $this->_plugin->settings->GetColumnsSelected();
231
+ if ( ! empty( $sel_columns ) ) {
232
+ unset( $cols );
233
+ $sel_columns = (array) json_decode( $sel_columns );
234
+ foreach ( $sel_columns as $key => $value ) {
235
+ switch ( $key ) {
236
+ case 'alert_code':
237
+ $cols['type'] = __( 'Alert ID', 'wp-security-audit-log' );
238
+ break;
239
+ case 'type':
240
+ $cols['code'] = __( 'Type', 'wp-security-audit-log' );
241
+ break;
242
+ case 'date':
243
+ $cols['crtd'] = __( 'Date', 'wp-security-audit-log' );
244
+ break;
245
+ case 'username':
246
+ $cols['user'] = $name_column;
247
+ break;
248
+ case 'source_ip':
249
+ $cols['scip'] = __( 'Source IP', 'wp-security-audit-log' );
250
+ break;
251
+ case 'site':
252
+ $cols['site'] = __( 'Site', 'wp-security-audit-log' );
253
+ break;
254
+ case 'message':
255
+ $cols['mesg'] = __( 'Message', 'wp-security-audit-log' );
256
+ break;
257
+ }
258
+ }
259
+ }
260
+ if ( $this->_plugin->settings->IsDataInspectorEnabled() ) {
261
+ $cols['data'] = '';
262
+ }
263
+ return $cols;
264
+ }
265
+
266
+ /**
267
+ * Method: Get checkbox column.
268
+ *
269
+ * @param object $item - Item.
270
+ * @return string
271
+ */
272
+ public function column_cb( $item ) {
273
+ return '<input type="checkbox" value="' . $item->id . '" '
274
+ . 'name="' . esc_attr( $this->_args['singular'] ) . '[]"/>';
275
+ }
276
+
277
+ /**
278
+ * Method: Get Sortable Columns.
279
+ *
280
+ * @return array
281
+ */
282
+ public function get_sortable_columns() {
283
+ return array(
284
+ 'read' => array( 'is_read', false ),
285
+ 'type' => array( 'alert_id', false ),
286
+ 'crtd' => array( 'created_on', true ),
287
+ 'user' => array( 'user', true ),
288
+ 'scip' => array( 'scip', false ),
289
+ );
290
+ }
291
+
292
+ /**
293
+ * Method: Get default column values.
294
+ *
295
+ * @param object $item - Column item.
296
+ * @param string $column_name - Name of the column.
297
+ */
298
+ public function column_default( $item, $column_name ) {
299
+ // Get date format.
300
+ $datetime_format = $this->_plugin->settings->GetDatetimeFormat();
301
+
302
+ switch ( $column_name ) {
303
+ case 'read':
304
+ return '<span class="log-read log-read-'
305
+ . ($item->is_read ? 'old' : 'new')
306
+ . '" title="' . __( 'Click to toggle.', 'wp-security-audit-log' ) . '"></span>';
307
+ case 'type':
308
+ $code = $this->_plugin->alerts->GetAlert( $item->alert_id );
309
+ $extra_msg = '';
310
+ $data_link = '';
311
+ $modification_alerts = array( 1002, 1003, 6007, 6023 );
312
+ if ( in_array( $item->alert_id, $modification_alerts, true ) ) {
313
+ $extra_msg = '. Modify this alert.';
314
+ if ( 1002 === $item->alert_id || 1003 === $item->alert_id ) {
315
+ $data_link = add_query_arg( 'page', 'wsal-togglealerts#tab-users-profiles---activity', admin_url( 'admin.php' ) );
316
+ } elseif ( 6007 === $item->alert_id || 6023 === $item->alert_id ) {
317
+ $data_link = add_query_arg( 'page', 'wsal-togglealerts#tab-system-activity', admin_url( 'admin.php' ) );
318
+ }
319
+ }
320
+
321
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'edit' ) ) {
322
+ return '<span class="log-disable">' . str_pad( $item->alert_id, 4, '0', STR_PAD_LEFT ) . ' </span>';
323
+ }
324
+
325
+ return '<span class="log-disable" data-disable-alert-nonce="' . wp_create_nonce( 'disable-alert-nonce' . $item->alert_id ) . '" data-tooltip="' . __( 'Disable this type of alerts.', 'wp-security-audit-log' ) . '<br>' . $item->alert_id . ' - ' . esc_html( $code->desc ) . $extra_msg . '" data-alert-id="' . $item->alert_id . '" ' . esc_attr( 'data-link=' . $data_link ) . ' >'
326
+ . str_pad( $item->alert_id, 4, '0', STR_PAD_LEFT ) . ' </span>';
327
+ case 'code':
328
+ $code = $this->_plugin->alerts->GetAlert( $item->alert_id );
329
+ $code = $code ? $code->code : 0;
330
+ $const = (object) array(
331
+ 'name' => 'E_UNKNOWN',
332
+ 'value' => 0,
333
+ 'description' => __( 'Unknown error code.', 'wp-security-audit-log' ),
334
+ );
335
+ $const = $this->_plugin->constants->GetConstantBy( 'value', $code, $const );
336
+ if ( 'E_CRITICAL' == $const->name ) {
337
+ $const->name = 'Critical';
338
+ } elseif ( 'E_WARNING' == $const->name ) {
339
+ $const->name = 'Warning';
340
+ } elseif ( 'E_NOTICE' == $const->name ) {
341
+ $const->name = 'Notification';
342
+ }
343
+ return '<a class="tooltip" href="#" data-tooltip="' . esc_html( $const->name ) . '"><span class="log-type log-type-' . $const->value
344
+ . '"></span></a>';
345
+ case 'crtd':
346
+ return $item->created_on ? (
347
+ str_replace(
348
+ '$$$',
349
+ substr( number_format( fmod( $item->created_on + $this->_gmt_offset_sec, 1 ), 3 ), 2 ),
350
+ date( $datetime_format, $item->created_on + $this->_gmt_offset_sec )
351
+ )
352
+ ) : '<i>unknown</i>';
353
+ case 'user':
354
+ $username = $item->GetUsername();
355
+ $type_name = $this->_plugin->settings->get_type_username();
356
+ if ( $username && ( $user = get_user_by( 'login', $username ) ) ) {
357
+ $image = get_avatar( $user->ID, 32 );
358
+ if ( 'display_name' == $type_name ) {
359
+ $display_name = $user->first_name . ' ' . $user->last_name;
360
+ } elseif ( 'username' == $type_name ) {
361
+ $display_name = $user->user_login;
362
+ }
363
+
364
+ if ( class_exists( 'WSAL_SearchExtension' ) ) {
365
+ $tooltip = esc_attr__( 'Show me all activity by this User', 'wp-security-audit-log' );
366
+
367
+ $uhtml = '<a class="search-user" data-tooltip="' . $tooltip . '" data-user="' . $user->user_login . '" href="' . admin_url( 'user-edit.php?user_id=' . $user->ID )
368
+ . '" target="_blank">' . esc_html( $display_name ) . '</a>';
369
+ } else {
370
+ $uhtml = '<a href="' . admin_url( 'user-edit.php?user_id=' . $user->ID )
371
+ . '" target="_blank">' . esc_html( $display_name ) . '</a>';
372
+ }
373
+
374
+ $roles = $item->GetUserRoles();
375
+ if ( is_array( $roles ) && count( $roles ) ) {
376
+ $roles = esc_html( ucwords( implode( ', ', $roles ) ) );
377
+ } elseif ( is_string( $roles ) && '' != $roles ) {
378
+ $roles = esc_html( ucwords( str_replace( array( '"', '[', ']' ), ' ', $roles ) ) );
379
+ } else {
380
+ $roles = '<i>' . __( 'Unknown', 'wp-security-audit-log' ) . '</i>';
381
+ }
382
+ } elseif ( 'Plugin' == $username ) {
383
+ $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/plugin-logo.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
384
+ $uhtml = '<i>' . __( 'Plugin', 'wp-security-audit-log' ) . '</i>';
385
+ $roles = '';
386
+ } elseif ( 'Plugins' == $username ) {
387
+ $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/wordpress-logo-32.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
388
+ $uhtml = '<i>' . __( 'Plugins', 'wp-security-audit-log' ) . '</i>';
389
+ $roles = '';
390
+ } elseif ( 'Website Visitor' == $username ) {
391
+ $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/wordpress-logo-32.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
392
+ $uhtml = '<i>' . __( 'Website Visitor', 'wp-security-audit-log' ) . '</i>';
393
+ $roles = '';
394
+ } else {
395
+ $image = '<img src="' . $this->_plugin->GetBaseUrl() . '/img/wordpress-logo-32.png" class="avatar avatar-32 photo" width="32" height="32" alt=""/>';
396
+ $uhtml = '<i>' . __( 'System', 'wp-security-audit-log' ) . '</i>';
397
+ $roles = '';
398
+ }
399
+ return $image . $uhtml . '<br/>' . $roles;
400
+ case 'scip':
401
+ $scip = $item->GetSourceIP();
402
+ if ( is_string( $scip ) ) {
403
+ $scip = str_replace( array( '"', '[', ']' ), '', $scip );
404
+ }
405
+
406
+ $oips = array(); // $item->GetOtherIPs();
407
+
408
+ // If there's no IP...
409
+ if ( is_null( $scip ) || '' == $scip ) {
410
+ return '<i>unknown</i>';
411
+ }
412
+
413
+ // If there's only one IP...
414
+ $link = 'http://whatismyipaddress.com/ip/' . $scip . '?utm_source=plugin&utm_medium=referral&utm_campaign=WPSAL';
415
+ if ( class_exists( 'WSAL_SearchExtension' ) ) {
416
+ $tooltip = esc_attr__( 'Show me all activity originating from this IP Address', 'wp-security-audit-log' );
417
+
418
+ if ( count( $oips ) < 2 ) {
419
+ return "<a class='search-ip' data-tooltip='$tooltip' data-ip='$scip' target='_blank' href='$link'>" . esc_html( $scip ) . '</a>';
420
+ }
421
+ } else {
422
+ if ( count( $oips ) < 2 ) {
423
+ return "<a target='_blank' href='$link'>" . esc_html( $scip ) . '</a>';
424
+ }
425
+ }
426
+
427
+ // If there are many IPs...
428
+ if ( class_exists( 'WSAL_SearchExtension' ) ) {
429
+ $tooltip = esc_attr__( 'Show me all activity originating from this IP Address', 'wp-security-audit-log' );
430
+
431
+ $html = "<a class='search-ip' data-tooltip='$tooltip' data-ip='$scip' target='_blank' href='http://whatismyipaddress.com/ip/$scip'>" . esc_html( $scip ) . '</a> <a href="javascript:;" onclick="jQuery(this).hide().next().show();">(more&hellip;)</a><div style="display: none;">';
432
+ foreach ( $oips as $ip ) {
433
+ if ( $scip != $ip ) {
434
+ $html .= '<div>' . $ip . '</div>';
435
+ }
436
+ }
437
+ $html .= '</div>';
438
+ return $html;
439
+ } else {
440
+ $html = "<a target='_blank' href='http://whatismyipaddress.com/ip/$scip'>" . esc_html( $scip ) . '</a> <a href="javascript:;" onclick="jQuery(this).hide().next().show();">(more&hellip;)</a><div style="display: none;">';
441
+ foreach ( $oips as $ip ) {
442
+ if ( $scip != $ip ) {
443
+ $html .= '<div>' . $ip . '</div>';
444
+ }
445
+ }
446
+ $html .= '</div>';
447
+ return $html;
448
+ }
449
+
450
+ case 'site':
451
+ $info = get_blog_details( $item->site_id, true );
452
+ return ! $info ? ('Unknown Site ' . $item->site_id)
453
+ : ('<a href="' . esc_attr( $info->siteurl ) . '">' . esc_html( $info->blogname ) . '</a>');
454
+ case 'mesg':
455
+ return '<div id="Event' . $item->id . '">' . $item->GetMessage( array( $this, 'meta_formatter' ) ) . '</div>';
456
+ case 'data':
457
+ $url = admin_url( 'admin-ajax.php' ) . '?action=AjaxInspector&amp;occurrence=' . $item->id;
458
+ return '<a class="more-info thickbox" title="' . __( 'Alert Data Inspector', 'wp-security-audit-log' ) . '"'
459
+ . ' href="' . $url . '&amp;TB_iframe=true&amp;width=600&amp;height=550">&hellip;</a>';
460
+ default:
461
+ return isset( $item->$column_name )
462
+ ? esc_html( $item->$column_name )
463
+ : 'Column "' . esc_html( $column_name ) . '" not found';
464
+ }
465
+ }
466
+
467
+ /**
468
+ * Method: Reorder string items.
469
+ *
470
+ * @param object $a - Item to compare.
471
+ * @param object $b - Item to compare.
472
+ * @return int
473
+ */
474
+ public function reorder_items_str( $a, $b ) {
475
+ $result = strcmp( $a->{$this->_orderby}, $b->{$this->_orderby} );
476
+ return ( 'asc' === $this->_order ) ? $result : -$result;
477
+ }
478
+
479
+ /**
480
+ * Method: Reorder items.
481
+ *
482
+ * @param object $a - Item to compare.
483
+ * @param object $b - Item to compare.
484
+ * @return int
485
+ */
486
+ public function reorder_items_int( $a, $b ) {
487
+ $result = $a->{$this->_orderby} - $b->{$this->_orderby};
488
+ return ( 'asc' === $this->_order ) ? $result : -$result;
489
+ }
490
+
491
+ /**
492
+ * Method: Meta data formater.
493
+ *
494
+ * @param string $name - Name of the data.
495
+ * @param mix $value - Value of the data.
496
+ * @return string
497
+ */
498
+ public function meta_formatter( $name, $value ) {
499
+ switch ( true ) {
500
+ case '%Message%' == $name:
501
+ return esc_html( $value );
502
+
503
+ case '%PromoMessage%' == $name:
504
+ return '<p class="promo-alert">' . $value . '</p>';
505
+
506
+ case '%PromoLink%' == $name:
507
+ case '%CommentLink%' == $name:
508
+ case '%CommentMsg%' == $name:
509
+ return $value;
510
+
511
+ case '%MetaLink%' == $name:
512
+ if ( ! empty( $value ) ) {
513
+ return "<a href=\"#\" data-disable-custom-nonce='" . wp_create_nonce( 'disable-custom-nonce' . $value ) . "' onclick=\"WsalDisableCustom(this, '" . $value . "');\"> Exclude Custom Field from the Monitoring</a>";
514
+ } else {
515
+ return '';
516
+ }
517
+
518
+ case '%RevisionLink%' == $name:
519
+ return ' Click <a target="_blank" href="' . esc_url( $value ) . '">here</a> to see the content changes.';
520
+
521
+ case '%EditorLinkPost%' == $name:
522
+ return ' <a target="_blank" href="' . esc_url( $value ) . '">View the post</a>';
523
+
524
+ case '%EditorLinkPage%' == $name:
525
+ return ' <a target="_blank" href="' . esc_url( $value ) . '">View the page</a>';
526
+
527
+ case '%CategoryLink%' == $name:
528
+ return ' <a target="_blank" href="' . esc_url( $value ) . '">View the category</a>';
529
+
530
+ case '%TagLink%' == $name:
531
+ return ' <a target="_blank" href="' . esc_url( $value ) . '">View the tag</a>';
532
+
533
+ case '%EditorLinkForum%' == $name:
534
+ return ' <a target="_blank" href="' . esc_url( $value ) . '">View the forum</a>';
535
+
536
+ case '%EditorLinkTopic%' == $name:
537
+ return ' <a target="_blank" href="' . esc_url( $value ) . '">View the topic</a>';
538
+
539
+ case in_array( $name, array( '%MetaValue%', '%MetaValueOld%', '%MetaValueNew%' ) ):
540
+ return '<strong>' . (
541
+ strlen( $value ) > 50 ? (esc_html( substr( $value, 0, 50 ) ) . '&hellip;') : esc_html( $value )
542
+ ) . '</strong>';
543
+
544
+ case '%ClientIP%' == $name:
545
+ if ( is_string( $value ) ) {
546
+ return '<strong>' . str_replace( array( '"', '[', ']' ), '', $value ) . '</strong>';
547
+ } else {
548
+ return '<i>unknown</i>';
549
+ }
550
+
551
+ case '%LinkFile%' == $name:
552
+ if ( 'NULL' != $value ) {
553
+ return '<a href="' . esc_url( $value ) . '" download>Download the Log file</a>';
554
+ } else {
555
+ return 'Click <a href="' . esc_url( admin_url( 'admin.php?page=wsal-togglealerts#tab-system-activity' ) ) . '">here</a> to log such requests to file';
556
+ }
557
+
558
+ case '%LogFileLink%' === $name:
559
+ if ( ! empty( $value ) && 'on' === $this->_plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
560
+ return '<a href="' . esc_url( $value ) . '" download>Download the Log file</a>';
561
+ } elseif ( ! empty( $value ) ) {
562
+ return '<a href="' . esc_url( $value ) . '">Keep a record of the usernames</a>';
563
+ }
564
+ // Failed login file link.
565
+ case '%LogFileText%' === $name:
566
+ return esc_html( $value );
567
+ // Failed login file text.
568
+ case strncmp( $value, 'http://', 7 ) === 0:
569
+ case strncmp( $value, 'https://', 7 ) === 0:
570
+ return '<a href="' . esc_html( $value ) . '" title="' . esc_html( $value ) . '" target="_blank">' . esc_html( $value ) . '</a>';
571
+
572
+ default:
573
+ return '<strong>' . esc_html( $value ) . '</strong>';
574
+ }
575
+ }
576
+
577
+ /**
578
+ * Method: Check if multisite.
579
+ *
580
+ * @return bool
581
+ */
582
+ protected function is_multisite() {
583
+ return $this->_plugin->IsMultisite();
584
+ }
585
+
586
+ /**
587
+ * Method: Check if the blog is main blog.
588
+ *
589
+ * @return bool
590
+ */
591
+ protected function is_main_blog() {
592
+ return get_current_blog_id() == 1;
593
+ }
594
+
595
+ /**
596
+ * Method: Check if it is a specific view.
597
+ *
598
+ * @return bool
599
+ */
600
+ protected function is_specific_view() {
601
+ // Filter $_POST array for security.
602
+ $post_array = filter_input_array( INPUT_POST );
603
+
604
+ return isset( $post_array['wsal-cbid'] ) && '0' != $post_array['wsal-cbid'];
605
+ }
606
+
607
+ /**
608
+ * Method: Get a specific view.
609
+ *
610
+ * @return int
611
+ */
612
+ protected function get_specific_view() {
613
+ // Filter $_POST array for security.
614
+ $post_array = filter_input_array( INPUT_POST );
615
+
616
+ return isset( $post_array['wsal-cbid'] ) ? (int) $post_array['wsal-cbid'] : 0;
617
+ }
618
+
619
+ /**
620
+ * Method: Get view site id.
621
+ *
622
+ * @return int
623
+ */
624
+ protected function get_view_site_id() {
625
+ switch ( true ) {
626
+ // Non-multisite.
627
+ case ! $this->is_multisite():
628
+ return 0;
629
+ // Multisite + main site view.
630
+ case $this->is_main_blog() && ! $this->is_specific_view():
631
+ return 0;
632
+ // Multisite + switched site view.
633
+ case $this->is_main_blog() && $this->is_specific_view():
634
+ return $this->get_specific_view();
635
+ // Multisite + local site view.
636
+ default:
637
+ return get_current_blog_id();
638
+ }
639
+ }
640
+
641
+ /**
642
+ * Method: Prepare items.
643
+ */
644
+ public function prepare_items() {
645
+ if ( $this->_plugin->settings->IsArchivingEnabled() ) {
646
+ // Switch to Archive DB.
647
+ $selected_db = get_transient( 'wsal_wp_selected_db' );
648
+ if ( $selected_db && 'archive' == $selected_db ) {
649
+ $this->_plugin->settings->SwitchToArchiveDB();
650
+ }
651
+ }
652
+
653
+ $per_page = $this->_plugin->settings->GetViewPerPage();
654
+
655
+ $columns = $this->get_columns();
656
+ $hidden = array();
657
+ $sortable = $this->get_sortable_columns();
658
+
659
+ $this->_column_headers = array( $columns, $hidden, $sortable );
660
+
661
+ // $this->process_bulk_action();
662
+ // TO DO: Get rid of OccurrenceQuery and use the Occurence Model.
663
+ $query = new WSAL_Models_OccurrenceQuery();
664
+
665
+ $bid = (int) $this->get_view_site_id();
666
+ if ( $bid ) {
667
+ $query->addCondition( 'site_id = %s ', $bid );
668
+ }
669
+
670
+ $query = apply_filters( 'wsal_auditlog_query', $query );
671
+
672
+ $total_items = $query->getAdapter()->Count( $query );
673
+
674
+ // Filter $_GET and $_POST arrays for security.
675
+ $get_array = filter_input_array( INPUT_GET );
676
+
677
+ if ( empty( $get_array['orderby'] ) ) {
678
+ $query->addOrderBy( 'created_on', true );
679
+ } else {
680
+ $order_by_field = $get_array['orderby'];
681
+
682
+ $is_descending = true;
683
+ if ( ! empty( $get_array['order'] ) && 'asc' == $get_array['order'] ) {
684
+ $is_descending = false;
685
+ }
686
+
687
+ // TO DO: Allow order by meta values.
688
+ if ( 'scip' == $order_by_field ) {
689
+ $query->addMetaJoin();
690
+ $query->addOrderBy( 'CASE WHEN meta.name = "ClientIP" THEN meta.value END', $is_descending );
691
+ } elseif ( 'user' == $order_by_field ) {
692
+ $query->addMetaJoin();
693
+ $query->addOrderBy( 'CASE WHEN meta.name = "CurrentUserID" THEN meta.value END', $is_descending );
694
+ } else {
695
+ $tmp = new WSAL_Models_Occurrence();
696
+ // Making sure the field exists to order by.
697
+ if ( isset( $tmp->{$order_by_field} ) ) {
698
+ // TODO: We used to use a custom comparator ... is it safe to let MySQL do the ordering now?.
699
+ $query->addOrderBy( $get_array['orderby'], $is_descending );
700
+
701
+ } else {
702
+ $query->addOrderBy( 'created_on', true );
703
+ }
704
+ }
705
+ }
706
+
707
+ // @todo Modify $query instead
708
+ // @deprecated
709
+ // $data = array_slice($data, ($this->get_pagenum() - 1) * $per_page, $per_page);
710
+ $query->setOffset( ($this->get_pagenum() - 1) * $per_page );
711
+ $query->setLimit( $per_page );
712
+
713
+ $this->items = $query->getAdapter()->Execute( $query );
714
+
715
+ $this->set_pagination_args(
716
+ array(
717
+ 'total_items' => $total_items,
718
+ 'per_page' => $per_page,
719
+ 'total_pages' => ceil( $total_items / $per_page ),
720
+ )
721
+ );
722
+ }
723
+
724
+ /**
725
+ * Method: Output Single row.
726
+ *
727
+ * @param object $item - Item.
728
+ */
729
+ public function single_row( $item ) {
730
+ if ( 9999 == $item->alert_id ) {
731
+ echo '<tr style="background-color: #D5E46E">';
732
+ $this->single_row_columns( $item );
733
+ echo '</tr>';
734
+ } else {
735
+ parent::single_row( $item );
736
+ }
737
+ }
738
  }
classes/Autoloader.php CHANGED
@@ -1,76 +1,93 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
- *
5
  * Classes Auto Loader.
 
 
6
  */
7
  class WSAL_Autoloader {
8
- /**
9
- * @var WpSecurityAuditLog
10
- */
11
- protected $plugin;
12
-
13
- protected $paths = array();
14
-
15
- public function __construct(WpSecurityAuditLog $plugin)
16
- {
17
- $this->plugin = $plugin;
18
-
19
- // register autoloader
20
- spl_autoload_register(array($this, 'LoadClass'));
21
- }
22
-
23
- public function Register($prefix, $path)
24
- {
25
- if (!isset($this->paths[$prefix])) {
26
- $this->paths[$prefix] = array();
27
- }
28
- $this->paths[$prefix][] = rtrim(str_replace('\\', '/', $path), '/') . '/';
29
- }
30
-
31
- /**
32
- * This is the class autoloader. You should not call this directly.
33
- * @param string $class Class name.
34
- * @return boolean True if class is found and loaded, false otherwise.
35
- */
36
- public function LoadClass($class)
37
- {
38
- foreach ($this->paths as $prefix => $paths) {
39
- foreach ($paths as $path) {
40
- if (strstr($class, $prefix) !== false) {
41
- $file = $path . str_replace('_', DIRECTORY_SEPARATOR, substr($class, strlen($prefix))) . '.php';
42
- if (file_exists($file)) {
43
- $s = $this->plugin->profiler->Start('Autoload ' . basename($file));
44
- require_once($file);
45
- $s->Stop();
46
- return class_exists($class, false) || interface_exists($class, false);
47
- }
48
- }
49
- }
50
- }
51
- return false;
52
- }
53
-
54
- /**
55
- * Returns the class name of a particular file that contains the class.
56
- * @param string $file File name.
57
- * @return string|false Class name or false on error.
58
- */
59
- public function GetClassFileClassName($file)
60
- {
61
- $file = str_replace('\\', '/', $file); // win/dos hotfix
62
-
63
- foreach ($this->paths as $prefix => $paths) {
64
- foreach ($paths as $path) {
65
- if (strstr($file, $path) !== false) {
66
- return str_replace(
67
- array($path, '/'),
68
- array($prefix, '_'),
69
- substr($file, 0, -4) // remove '.php'
70
- );
71
- }
72
- }
73
- }
74
- return false;
75
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
1
  <?php
2
  /**
 
 
3
  * Classes Auto Loader.
4
+ *
5
+ * @package Wsal
6
  */
7
  class WSAL_Autoloader {
8
+
9
+ /**
10
+ * Instance of WpSecurityAuditLog.
11
+ *
12
+ * @var object
13
+ */
14
+ protected $plugin;
15
+
16
+ /**
17
+ * Paths to load.
18
+ *
19
+ * @var array
20
+ */
21
+ protected $paths = array();
22
+
23
+ /**
24
+ * Method: Constructor.
25
+ *
26
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
27
+ */
28
+ public function __construct( WpSecurityAuditLog $plugin ) {
29
+ $this->plugin = $plugin;
30
+
31
+ // Register autoloader.
32
+ spl_autoload_register( array( $this, 'LoadClass' ) );
33
+ }
34
+
35
+ /**
36
+ * Method: Register class.
37
+ *
38
+ * @param string $prefix - Prefix of the class.
39
+ * @param string $path - Path of the file.
40
+ */
41
+ public function Register( $prefix, $path ) {
42
+ if ( ! isset( $this->paths[ $prefix ] ) ) {
43
+ $this->paths[ $prefix ] = array();
44
+ }
45
+ $this->paths[ $prefix ][] = rtrim( str_replace( '\\', '/', $path ), '/' ) . '/';
46
+ }
47
+
48
+ /**
49
+ * This is the class autoloader. You should not call this directly.
50
+ *
51
+ * @param string $class - Class name.
52
+ * @return boolean - True if class is found and loaded, false otherwise.
53
+ */
54
+ public function LoadClass( $class ) {
55
+ foreach ( $this->paths as $prefix => $paths ) {
56
+ foreach ( $paths as $path ) {
57
+ if ( strstr( $class, $prefix ) !== false ) {
58
+ $file = $path . str_replace( '_', DIRECTORY_SEPARATOR, substr( $class, strlen( $prefix ) ) ) . '.php';
59
+ if ( file_exists( $file ) ) {
60
+ $s = $this->plugin->profiler->Start( 'Autoload ' . basename( $file ) );
61
+ require_once( $file );
62
+ $s->Stop();
63
+ return class_exists( $class, false ) || interface_exists( $class, false );
64
+ }
65
+ }
66
+ }
67
+ }
68
+ return false;
69
+ }
70
+
71
+ /**
72
+ * Returns the class name of a particular file that contains the class.
73
+ *
74
+ * @param string $file File name.
75
+ * @return string|false Class name or false on error.
76
+ */
77
+ public function GetClassFileClassName( $file ) {
78
+ $file = str_replace( '\\', '/', $file ); // Win/DOS hotfix.
79
+
80
+ foreach ( $this->paths as $prefix => $paths ) {
81
+ foreach ( $paths as $path ) {
82
+ if ( strstr( $file, $path ) !== false ) {
83
+ return str_replace(
84
+ array( $path, '/' ),
85
+ array( $prefix, '_' ),
86
+ substr( $file, 0, -4 ) // Remove '.php'.
87
+ );
88
+ }
89
+ }
90
+ }
91
+ return false;
92
+ }
93
  }
classes/Connector/AbstractConnector.php CHANGED
@@ -1,43 +1,80 @@
1
  <?php
2
- //require_once('ConnectorInterface.php');
3
- require_once('wp-db-custom.php');
4
  /**
 
 
 
 
5
  * @package Wsal
 
 
 
 
 
 
 
 
 
 
 
6
  * Adapter Classes loader class.
7
  *
8
  * Abstract class used as a class loader.
 
 
9
  */
10
- abstract class WSAL_Connector_AbstractConnector
11
- {
12
- protected $connection = null;
13
- protected $adaptersBasePath = null;
14
- protected $adaptersDirName = null;
15
-
16
- public function __construct($adaptersDirName = null)
17
- {
18
- $this->adaptersBasePath = __DIR__ . DIRECTORY_SEPARATOR .'..'. DIRECTORY_SEPARATOR .'Adapters'. DIRECTORY_SEPARATOR;
19
-
20
- //require_once($this->adaptersBasePath . 'ActiveRecordInterface.php');
21
- //require_once($this->adaptersBasePath . 'MetaInterface.php');
22
- //require_once($this->adaptersBasePath . 'OccurrenceInterface.php');
23
- //require_once($this->adaptersBasePath . 'QueryInterface.php');
24
-
25
- if (!empty($adaptersDirName)) {
26
- $this->adaptersDirName = $adaptersDirName;
27
- require_once($this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'ActiveRecordAdapter.php');
28
- require_once($this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'MetaAdapter.php');
29
- require_once($this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'OccurrenceAdapter.php');
30
- require_once($this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'QueryAdapter.php');
31
- require_once($this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'TmpUserAdapter.php');
32
- }
33
- }
34
-
35
- public function getAdaptersDirectory()
36
- {
37
- if (!empty($this->adaptersBasePath) && !empty($this->adaptersDirName)) {
38
- return $this->adaptersBasePath . $this->adaptersDirName;
39
- } else {
40
- return false;
41
- }
42
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
1
  <?php
 
 
2
  /**
3
+ * Class: Abstract Connector.
4
+ *
5
+ * Abstract class used as a class loader.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ // require_once('ConnectorInterface.php');.
16
+ require_once( 'wp-db-custom.php' );
17
+
18
+ /**
19
  * Adapter Classes loader class.
20
  *
21
  * Abstract class used as a class loader.
22
+ *
23
+ * @package Wsal
24
  */
25
+ abstract class WSAL_Connector_AbstractConnector {
26
+
27
+ /**
28
+ * Connection Variable.
29
+ *
30
+ * @var null
31
+ */
32
+ protected $connection = null;
33
+
34
+ /**
35
+ * Adapter Base Path.
36
+ *
37
+ * @var null
38
+ */
39
+ protected $adaptersBasePath = null;
40
+
41
+ /**
42
+ * Adapter Directory Name.
43
+ *
44
+ * @var null
45
+ */
46
+ protected $adaptersDirName = null;
47
+
48
+ /**
49
+ * Method: Constructor.
50
+ *
51
+ * @param string $adapters_dir_name - Adapter directory name.
52
+ */
53
+ public function __construct( $adapters_dir_name = null ) {
54
+ $this->adaptersBasePath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'Adapters' . DIRECTORY_SEPARATOR;
55
+
56
+ // require_once($this->adaptersBasePath . 'ActiveRecordInterface.php');
57
+ // require_once($this->adaptersBasePath . 'MetaInterface.php');
58
+ // require_once($this->adaptersBasePath . 'OccurrenceInterface.php');
59
+ // require_once($this->adaptersBasePath . 'QueryInterface.php');
60
+ if ( ! empty( $adapters_dir_name ) ) {
61
+ $this->adaptersDirName = $adapters_dir_name;
62
+ require_once( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'ActiveRecordAdapter.php' );
63
+ require_once( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'MetaAdapter.php' );
64
+ require_once( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'OccurrenceAdapter.php' );
65
+ require_once( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'QueryAdapter.php' );
66
+ require_once( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . 'TmpUserAdapter.php' );
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Method: Get adapters directory.
72
+ */
73
+ public function getAdaptersDirectory() {
74
+ if ( ! empty( $this->adaptersBasePath ) && ! empty( $this->adaptersDirName ) ) {
75
+ return $this->adaptersBasePath . $this->adaptersDirName;
76
+ } else {
77
+ return false;
78
+ }
79
+ }
80
  }
classes/Connector/ConnectorFactory.php CHANGED
@@ -1,115 +1,147 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * Class WSAL_Connector_ConnectorFactory.
5
  *
6
  * Abstract class used for create the connector, only MySQL is implemented.
7
- * @todo add other adapters.
 
 
8
  */
9
- abstract class WSAL_Connector_ConnectorFactory
10
- {
11
- public static $connector;
12
- public static $defaultConnector;
13
- public static $adapter;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- /**
16
- * Returns the a default WPDB connector for saving options
17
- */
18
- public static function GetDefaultConnector()
19
- {
20
- return new WSAL_Connector_MySQLDB();
21
- }
 
 
 
 
 
 
22
 
23
- /**
24
- * Returns a connector singleton
25
- * @return WSAL_Connector_ConnectorInterface
26
- */
27
- public static function GetConnector($config = null, $reset = false)
28
- {
29
- if (!empty($config)) {
30
- $connectionConfig = $config;
31
- } else {
32
- $connectionConfig = self::GetConfig();
33
- }
34
-
35
- //TO DO: Load connection config
36
- if (self::$connector == null || !empty($config) || $reset) {
37
- switch (strtolower($connectionConfig['type'])) {
38
- //TO DO: Add other connectors
39
- case 'mysql':
40
- default:
41
- //use config
42
- self::$connector = new WSAL_Connector_MySQLDB($connectionConfig);
43
- }
44
- }
45
- return self::$connector;
46
- }
47
 
48
- /**
49
- * Get the adapter config stored in the DB
50
- * @return array|null adapter config
51
- */
52
- public static function GetConfig()
53
- {
54
- $conf = new WSAL_Settings(WpSecurityAuditLog::GetInstance());
55
- $type = $conf->GetAdapterConfig('adapter-type');
56
- if (empty($type)) {
57
- return null;
58
- } else {
59
- return array(
60
- 'type' => $conf->GetAdapterConfig('adapter-type'),
61
- 'user' => $conf->GetAdapterConfig('adapter-user'),
62
- 'password' => $conf->GetAdapterConfig('adapter-password'),
63
- 'name' => $conf->GetAdapterConfig('adapter-name'),
64
- 'hostname' => $conf->GetAdapterConfig('adapter-hostname'),
65
- 'base_prefix' => $conf->GetAdapterConfig('adapter-base-prefix')
66
- );
67
- }
68
- }
69
 
70
- /**
71
- * Check the adapter config with a test connection.
72
- * @param string $type adapter type
73
- * @param string $user adapter user
74
- * @param string $password adapter password
75
- * @param string $name adapter name
76
- * @param string $hostname adapter hostname
77
- * @param string $base_prefix adapter base_prefix
78
- * @return boolean true|false
79
- */
80
- public static function CheckConfig($type, $user, $password, $name, $hostname, $base_prefix)
81
- {
82
- $result = false;
83
- $config = self::GetConfigArray($type, $user, $password, $name, $hostname, $base_prefix);
84
- switch (strtolower($type)) {
85
- //TO DO: Add other connectors
86
- case 'mysql':
87
- default:
88
- $test = new WSAL_Connector_MySQLDB($config);
89
- $result = $test->TestConnection();
90
- }
91
- return $result;
92
- }
93
 
94
- /**
95
- * Create array config.
96
- * @param string $type adapter type
97
- * @param string $user adapter user
98
- * @param string $password adapter password
99
- * @param string $name adapter name
100
- * @param string $hostname adapter hostname
101
- * @param string $base_prefix adapter base_prefix
102
- * @return array config
103
- */
104
- public static function GetConfigArray($type, $user, $password, $name, $hostname, $base_prefix)
105
- {
106
- return array(
107
- 'type' => $type,
108
- 'user' => $user,
109
- 'password' => $password,
110
- 'name' => $name,
111
- 'hostname' => $hostname,
112
- 'base_prefix' => $base_prefix
113
- );
114
- }
115
  }
1
  <?php
2
  /**
3
+ * Class: Abstract Connector Factory.
4
+ *
5
+ * Abstract class used for create the connector, only MySQL is implemented.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * Class WSAL_Connector_ConnectorFactory.
17
  *
18
  * Abstract class used for create the connector, only MySQL is implemented.
19
+ *
20
+ * @todo Add other adapters.
21
+ * @package Wsal
22
  */
23
+ abstract class WSAL_Connector_ConnectorFactory {
24
+
25
+ /**
26
+ * Connector.
27
+ *
28
+ * @var array
29
+ */
30
+ public static $connector;
31
+
32
+ /**
33
+ * Default Connector.
34
+ *
35
+ * @var bool
36
+ */
37
+ public static $defaultConnector;
38
+
39
+ /**
40
+ * Adapter.
41
+ *
42
+ * @var string
43
+ */
44
+ public static $adapter;
45
+
46
+ /**
47
+ * Returns the a default WPDB connector for saving options
48
+ */
49
+ public static function GetDefaultConnector() {
50
+ return new WSAL_Connector_MySQLDB();
51
+ }
52
 
53
+ /**
54
+ * Returns a connector singleton
55
+ *
56
+ * @param array $config - Connection config.
57
+ * @param bool $reset - True if reset.
58
+ * @return WSAL_Connector_ConnectorInterface
59
+ */
60
+ public static function GetConnector( $config = null, $reset = false ) {
61
+ if ( ! empty( $config ) ) {
62
+ $connection_config = $config;
63
+ } else {
64
+ $connection_config = self::GetConfig();
65
+ }
66
 
67
+ // TO DO: Load connection config.
68
+ if ( null == self::$connector || ! empty( $config ) || $reset ) {
69
+ switch ( strtolower( $connection_config['type'] ) ) {
70
+ // TO DO: Add other connectors.
71
+ case 'mysql':
72
+ default:
73
+ // Use config.
74
+ self::$connector = new WSAL_Connector_MySQLDB( $connection_config );
75
+ }
76
+ }
77
+ return self::$connector;
78
+ }
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ /**
81
+ * Get the adapter config stored in the DB
82
+ *
83
+ * @return array|null adapter config
84
+ */
85
+ public static function GetConfig() {
86
+ $conf = new WSAL_Settings( WpSecurityAuditLog::GetInstance() );
87
+ $type = $conf->GetAdapterConfig( 'adapter-type' );
88
+ if ( empty( $type ) ) {
89
+ return null;
90
+ } else {
91
+ return array(
92
+ 'type' => $conf->GetAdapterConfig( 'adapter-type' ),
93
+ 'user' => $conf->GetAdapterConfig( 'adapter-user' ),
94
+ 'password' => $conf->GetAdapterConfig( 'adapter-password' ),
95
+ 'name' => $conf->GetAdapterConfig( 'adapter-name' ),
96
+ 'hostname' => $conf->GetAdapterConfig( 'adapter-hostname' ),
97
+ 'base_prefix' => $conf->GetAdapterConfig( 'adapter-base-prefix' ),
98
+ );
99
+ }
100
+ }
101
 
102
+ /**
103
+ * Check the adapter config with a test connection.
104
+ *
105
+ * @param string $type - Adapter type.
106
+ * @param string $user - Adapter user.
107
+ * @param string $password - Adapter password.
108
+ * @param string $name - Adapter name.
109
+ * @param string $hostname - Adapter hostname.
110
+ * @param string $base_prefix - Adapter base_prefix.
111
+ * @return boolean true|false
112
+ */
113
+ public static function CheckConfig( $type, $user, $password, $name, $hostname, $base_prefix ) {
114
+ $result = false;
115
+ $config = self::GetConfigArray( $type, $user, $password, $name, $hostname, $base_prefix );
116
+ switch ( strtolower( $type ) ) {
117
+ // TO DO: Add other connectors.
118
+ case 'mysql':
119
+ default:
120
+ $test = new WSAL_Connector_MySQLDB( $config );
121
+ $result = $test->TestConnection();
122
+ }
123
+ return $result;
124
+ }
125
 
126
+ /**
127
+ * Create array config.
128
+ *
129
+ * @param string $type - Adapter type.
130
+ * @param string $user - Adapter user.
131
+ * @param string $password - Adapter password.
132
+ * @param string $name - Adapter name.
133
+ * @param string $hostname - Adapter hostname.
134
+ * @param string $base_prefix - Adapter base_prefix.
135
+ * @return array config
136
+ */
137
+ public static function GetConfigArray( $type, $user, $password, $name, $hostname, $base_prefix ) {
138
+ return array(
139
+ 'type' => $type,
140
+ 'user' => $user,
141
+ 'password' => $password,
142
+ 'name' => $name,
143
+ 'hostname' => $hostname,
144
+ 'base_prefix' => $base_prefix,
145
+ );
146
+ }
147
  }
classes/Connector/ConnectorInterface.php CHANGED
@@ -1,16 +1,58 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * Interface used by the WSAL_Connector.
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  */
7
- interface WSAL_Connector_ConnectorInterface
8
- {
9
- public function getAdapter($class_name);
10
- public function getConnection();
11
- public function closeConnection();
12
- public function isInstalled();
13
- public function canMigrate();
14
- public function installAll();
15
- public function uninstallAll();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
1
  <?php
2
  /**
3
+ * Class: Connection Interface
4
  *
5
  * Interface used by the WSAL_Connector.
6
+ *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Interface used by the WSAL_Connector.
17
+ *
18
+ * @package Wsal
19
  */
20
+ interface WSAL_Connector_ConnectorInterface {
21
+
22
+ /**
23
+ * Gets the adapter.
24
+ *
25
+ * @param string $class_name - Class name.
26
+ */
27
+ public function getAdapter( $class_name );
28
+
29
+ /**
30
+ * Get the connection.
31
+ */
32
+ public function getConnection();
33
+
34
+ /**
35
+ * Close the connection.
36
+ */
37
+ public function closeConnection();
38
+
39
+ /**
40
+ * Is installed?
41
+ */
42
+ public function isInstalled();
43
+
44
+ /**
45
+ * Can migrate?
46
+ */
47
+ public function canMigrate();
48
+
49
+ /**
50
+ * Install all.
51
+ */
52
+ public function installAll();
53
+
54
+ /**
55
+ * Uninstall all.
56
+ */
57
+ public function uninstallAll();
58
  }
classes/Connector/MySQLDB.php CHANGED
@@ -1,673 +1,688 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * MySQL Connector Class
5
  * It uses wpdb WordPress DB Class
 
 
6
  */
7
- class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements WSAL_Connector_ConnectorInterface
8
- {
9
- protected $connectionConfig = null;
10
-
11
- /**
12
- * @param string $dbuser MySQL database user
13
- * @param string $dbpassword MySQL database password
14
- * @param string $dbname MySQL database name
15
- * @param string $dbhost MySQL database host
16
- */
17
- public function __construct($connectionConfig = null)
18
- {
19
- $this->connectionConfig = $connectionConfig;
20
- parent::__construct("MySQL");
21
- require_once($this->getAdaptersDirectory() . '/OptionAdapter.php');
22
- }
23
-
24
- /**
25
- * Test the connection.
26
- * @throws Exception Connection failed
27
- */
28
- public function TestConnection()
29
- {
30
- error_reporting(E_ALL ^ (E_NOTICE | E_WARNING | E_DEPRECATED));
31
- $connectionConfig = $this->connectionConfig;
32
- $password = $this->decryptString($connectionConfig['password']);
33
- $newWpdb = new wpdbCustom($connectionConfig['user'], $password, $connectionConfig['name'], $connectionConfig['hostname']);
34
- if (!$newWpdb->has_connected) { // Database Error
35
- throw new Exception("Connection failed. Please check your connection details.");
36
- }
37
- }
38
-
39
- /**
40
- * Creates a connection and returns it
41
- * @return Instance of WPDB
42
- */
43
- private function createConnection()
44
- {
45
- if (!empty($this->connectionConfig)) {
46
- //TO DO: Use the provided connection config
47
- $connectionConfig = $this->connectionConfig;
48
- $password = $this->decryptString($connectionConfig['password']);
49
- $newWpdb = new wpdb($connectionConfig['user'], $password, $connectionConfig['name'], $connectionConfig['hostname']);
50
- $newWpdb->set_prefix($connectionConfig['base_prefix']);
51
- return $newWpdb;
52
- } else {
53
- global $wpdb;
54
- return $wpdb;
55
- }
56
- }
57
-
58
- /**
59
- * Returns a wpdb instance
60
- * @return wpdb
61
- */
62
- public function getConnection()
63
- {
64
- if (!empty($this->connection)) {
65
- return $this->connection;
66
- } else {
67
- $this->connection = $this->createConnection();
68
- return $this->connection;
69
- }
70
- }
71
-
72
- /**
73
- * Close DB connection
74
- */
75
- public function closeConnection()
76
- {
77
- $currentWpdb = $this->getConnection();
78
- $result = $currentWpdb->close();
79
- return $result;
80
- }
81
-
82
- /**
83
- * Gets an adapter for the specified model.
84
- * @return WSAL_Adapters_MySQL_{class_name}
85
- */
86
- public function getAdapter($class_name)
87
- {
88
- $objName = $this->getAdapterClassName($class_name);
89
- return new $objName($this->getConnection());
90
- }
91
-
92
- protected function getAdapterClassName($class_name)
93
- {
94
- return 'WSAL_Adapters_MySQL_'.$class_name;
95
- }
96
-
97
- /**
98
- * Checks if the necessary tables are available
99
- * @return bool true|false
100
- */
101
- public function isInstalled()
102
- {
103
- global $wpdb;
104
- $table = $wpdb->base_prefix . 'wsal_occurrences';
105
- return ($wpdb->get_var('SHOW TABLES LIKE "'.$table.'"') == $table);
106
- }
107
-
108
- /**
109
- * Checks if old version tables are available
110
- * @return bool true|false
111
- */
112
- public function canMigrate()
113
- {
114
- $wpdb = $this->getConnection();
115
- $table = $wpdb->base_prefix . 'wordpress_auditlog_events';
116
- return ($wpdb->get_var('SHOW TABLES LIKE "'.$table.'"') == $table);
117
- }
118
-
119
- /**
120
- * Install all DB tables.
121
- */
122
- public function installAll($excludeOptions = false)
123
- {
124
- $plugin = WpSecurityAuditLog::GetInstance();
125
-
126
- foreach (glob($this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . '*.php') as $file) {
127
- $filePath = explode(DIRECTORY_SEPARATOR, $file);
128
- $fileName = $filePath[count($filePath) - 1];
129
- $className = $this->getAdapterClassName(str_replace("Adapter.php", "", $fileName));
130
-
131
- $class = new $className($this->getConnection());
132
- if ($excludeOptions && $class instanceof WSAL_Adapters_MySQL_Option) {
133
- continue;
134
- }
135
-
136
- // exclude the tmp_users table
137
- if (!$excludeOptions && $class instanceof WSAL_Adapters_MySQL_TmpUser) {
138
- continue;
139
- }
140
-
141
- if (is_subclass_of($class, "WSAL_Adapters_MySQL_ActiveRecord")) {
142
- $class->Install();
143
- }
144
- }
145
- }
146
-
147
- /**
148
- * Uninstall all DB tables.
149
- */
150
- public function uninstallAll()
151
- {
152
- $plugin = WpSecurityAuditLog::GetInstance();
153
-
154
- foreach (glob($this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . '*.php') as $file) {
155
- $filePath = explode(DIRECTORY_SEPARATOR, $file);
156
- $fileName = $filePath[count($filePath) - 1];
157
- $className = $this->getAdapterClassName(str_replace("Adapter.php", "", $fileName));
158
-
159
- $class = new $className($this->getConnection());
160
- if (is_subclass_of($class, "WSAL_Adapters_MySQL_ActiveRecord")) {
161
- $class->Uninstall();
162
- }
163
- }
164
- }
165
-
166
- /**
167
- * Increase occurrence ID
168
- * @return integer MAX(id)
169
- */
170
- private function GetIncreaseOccurrence()
171
- {
172
- $_wpdb = $this->getConnection();
173
- $occurrenceNew = new WSAL_Adapters_MySQL_Occurrence($_wpdb);
174
- $sql = 'SELECT MAX(id) FROM ' . $occurrenceNew->GetTable();
175
- return (int)$_wpdb->get_var($sql);
176
- }
177
-
178
- /**
179
- * Migrate Metadata from WP DB to External DB.
180
- * @param integer $index
181
- * @param integer $limit limit
182
- */
183
- public function MigrateMeta($index, $limit)
184
- {
185
- $result = null;
186
- $offset = ($index * $limit);
187
- global $wpdb;
188
- $_wpdb = $this->getConnection();
189
- // Add +1 because an alert is generated after delete the metadata table
190
- $increase_occurrence_id = $this->GetIncreaseOccurrence() + 1;
191
-
192
- // Load data Meta from WP
193
- $meta = new WSAL_Adapters_MySQL_Meta($wpdb);
194
- if (!$meta->IsInstalled()) {
195
- $result['empty'] = true;
196
- return $result;
197
- }
198
- $sql = 'SELECT * FROM ' . $meta->GetWPTable() . ' LIMIT ' . $limit . ' OFFSET '. $offset;
199
- $metadata = $wpdb->get_results($sql, ARRAY_A);
200
-
201
- // Insert data to External DB
202
- if (!empty($metadata)) {
203
- $metaNew = new WSAL_Adapters_MySQL_Meta($_wpdb);
204
-
205
- $index++;
206
- $sql = 'INSERT INTO ' . $metaNew->GetTable() . ' (occurrence_id, name, value) VALUES ' ;
207
- foreach ($metadata as $entry) {
208
- $occurrence_id = intval($entry['occurrence_id']) + $increase_occurrence_id;
209
- $sql .= '('.$occurrence_id.', \''.$entry['name'].'\', \''.str_replace(array("'", "\'"), "\'", $entry['value']).'\'), ';
210
- }
211
- $sql = rtrim($sql, ", ");
212
- $_wpdb->query($sql);
213
-
214
- $result['complete'] = false;
215
- } else {
216
- $result['complete'] = true;
217
- $this->DeleteAfterMigrate($meta);
218
- }
219
- $result['index'] = $index;
220
- return $result;
221
- }
222
-
223
- /**
224
- * Migrate Occurrences from WP DB to External DB.
225
- * @param integer $index
226
- * @param integer $limit limit
227
- */
228
- public function MigrateOccurrence($index, $limit)
229
- {
230
- $result = null;
231
- $offset = ($index * $limit);
232
- global $wpdb;
233
- $_wpdb = $this->getConnection();
234
-
235
- // Load data Occurrences from WP
236
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($wpdb);
237
- if (!$occurrence->IsInstalled()) {
238
- $result['empty'] = true;
239
- return $result;
240
- }
241
- $sql = 'SELECT * FROM ' . $occurrence->GetWPTable() . ' LIMIT ' . $limit . ' OFFSET '. $offset;
242
- $occurrences = $wpdb->get_results($sql, ARRAY_A);
243
-
244
- // Insert data to External DB
245
- if (!empty($occurrences)) {
246
- $occurrenceNew = new WSAL_Adapters_MySQL_Occurrence($_wpdb);
247
-
248
- $index++;
249
- $sql = 'INSERT INTO ' . $occurrenceNew->GetTable() . ' (site_id, alert_id, created_on, is_read) VALUES ' ;
250
- foreach ($occurrences as $entry) {
251
- $sql .= '('.$entry['site_id'].', '.$entry['alert_id'].', '.$entry['created_on'].', '.$entry['is_read'].'), ';
252
- }
253
- $sql = rtrim($sql, ", ");
254
- $_wpdb->query($sql);
255
-
256
- $result['complete'] = false;
257
- } else {
258
- $result['complete'] = true;
259
- $this->DeleteAfterMigrate($occurrence);
260
- }
261
- $result['index'] = $index;
262
- return $result;
263
- }
264
-
265
- /**
266
- * Migrate Back Occurrences from External DB to WP DB.
267
- * @param integer $index
268
- * @param integer $limit limit
269
- */
270
- public function MigrateBackOccurrence($index, $limit)
271
- {
272
- $result = null;
273
- $offset = ($index * $limit);
274
- global $wpdb;
275
- $_wpdb = $this->getConnection();
276
-
277
- // Load data Occurrences from External DB
278
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($_wpdb);
279
- if (!$occurrence->IsInstalled()) {
280
- $result['empty'] = true;
281
- return $result;
282
- }
283
- $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' LIMIT ' . $limit . ' OFFSET '. $offset;
284
- $occurrences = $_wpdb->get_results($sql, ARRAY_A);
285
-
286
- // Insert data to WP
287
- if (!empty($occurrences)) {
288
- $occurrenceWP = new WSAL_Adapters_MySQL_Occurrence($wpdb);
289
-
290
- $index++;
291
- $sql = 'INSERT INTO ' . $occurrenceWP->GetWPTable() . ' (id, site_id, alert_id, created_on, is_read) VALUES ' ;
292
- foreach ($occurrences as $entry) {
293
- $sql .= '('.$entry['id'].', '.$entry['site_id'].', '.$entry['alert_id'].', '.$entry['created_on'].', '.$entry['is_read'].'), ';
294
- }
295
- $sql = rtrim($sql, ", ");
296
- $wpdb->query($sql);
297
-
298
- $result['complete'] = false;
299
- } else {
300
- $result['complete'] = true;
301
- }
302
- $result['index'] = $index;
303
- return $result;
304
- }
305
-
306
- /**
307
- * Migrate Back Metadata from External DB to WP DB.
308
- * @param integer $index
309
- * @param integer $limit limit
310
- */
311
- public function MigrateBackMeta($index, $limit)
312
- {
313
- $result = null;
314
- $offset = ($index * $limit);
315
- global $wpdb;
316
- $_wpdb = $this->getConnection();
317
-
318
- // Load data Meta from External DB
319
- $meta = new WSAL_Adapters_MySQL_Meta($_wpdb);
320
- if (!$meta->IsInstalled()) {
321
- $result['empty'] = true;
322
- return $result;
323
- }
324
- $sql = 'SELECT * FROM ' . $meta->GetTable() . ' LIMIT ' . $limit . ' OFFSET '. $offset;
325
- $metadata = $_wpdb->get_results($sql, ARRAY_A);
326
-
327
- // Insert data to WP
328
- if (!empty($metadata)) {
329
- $metaWP = new WSAL_Adapters_MySQL_Meta($wpdb);
330
-
331
- $index++;
332
- $sql = 'INSERT INTO ' . $metaWP->GetWPTable() . ' (occurrence_id, name, value) VALUES ' ;
333
- foreach ($metadata as $entry) {
334
- $sql .= '('.$entry['occurrence_id'].', \''.$entry['name'].'\', \''.str_replace(array("'", "\'"), "\'", $entry['value']).'\'), ';
335
- }
336
- $sql = rtrim($sql, ", ");
337
- $wpdb->query($sql);
338
-
339
- $result['complete'] = false;
340
- } else {
341
- $result['complete'] = true;
342
- }
343
- $result['index'] = $index;
344
- return $result;
345
- }
346
-
347
- /**
348
- * Delete after Migrate alerts.
349
- * @param object $record type of record
350
- */
351
- private function DeleteAfterMigrate($record)
352
- {
353
- global $wpdb;
354
- $sql = 'DROP TABLE IF EXISTS ' . $record->GetTable();
355
- $wpdb->query($sql);
356
- }
357
-
358
- /**
359
- * Encrypt plain text.
360
- * Encrypt string, before saves it to the DB.
361
- *
362
- * @param string $plaintext - Plain text that is going to be encrypted.
363
- * @return string
364
- * @since 2.6.3
365
- */
366
- public function encryptString( $plaintext ) {
367
-
368
- // Check for previous version.
369
- $plugin = WpSecurityAuditLog::GetInstance();
370
- $version = $plugin->GetGlobalOption( 'version', '0.0.0' );
371
-
372
- if ( -1 === version_compare( $version, '2.6.2' ) ) {
373
- return $this->encryptString_fallback( $plaintext );
374
- }
375
-
376
- $ciphertext = false;
377
-
378
- $encrypt_method = 'AES-256-CBC';
379
- $secret_key = $this->truncateKey();
380
- $secret_iv = $this->get_openssl_iv();
381
-
382
- // Hash the key.
383
- $key = hash( 'sha256', $secret_key );
384
-
385
- // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning.
386
- $iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );
387
-
388
- $ciphertext = openssl_encrypt( $plaintext, $encrypt_method, $key, 0, $iv );
389
- $ciphertext = base64_encode( $ciphertext );
390
-
391
- return $ciphertext;
392
-
393
- }
394
-
395
- /**
396
- * Encrypt plain text - Fallback.
397
- *
398
- * @param string $plaintext - Plain text that is going to be encrypted.
399
- * @return string
400
- * @since 2.6.3
401
- */
402
- public function encryptString_fallback( $plaintext ) {
403
-
404
- $iv_size = mcrypt_get_iv_size( MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC );
405
- $iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
406
- $key = $this->truncateKey();
407
- $ciphertext = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv );
408
- $ciphertext = $iv . $ciphertext;
409
- $ciphertext_base64 = base64_encode( $ciphertext );
410
- return $ciphertext_base64;
411
-
412
- }
413
-
414
- /**
415
- * Decrypt the encrypted string.
416
- * Decrypt string, after reads it from the DB.
417
- *
418
- * @param string $ciphertext_base64 - encrypted string.
419
- * @return string
420
- * @since 2.6.3
421
- */
422
- public function decryptString( $ciphertext_base64 ) {
423
-
424
- // Check for previous version.
425
- $plugin = WpSecurityAuditLog::GetInstance();
426
- $version = $plugin->GetGlobalOption( 'version', '0.0.0' );
427
-
428
- if ( -1 === version_compare( $version, '2.6.2' ) ) {
429
- return $this->decryptString_fallback( $ciphertext_base64 );
430
- }
431
-
432
- $plaintext = false;
433
-
434
- $encrypt_method = 'AES-256-CBC';
435
- $secret_key = $this->truncateKey();
436
- $secret_iv = $this->get_openssl_iv();
437
-
438
- // Hash the key.
439
- $key = hash( 'sha256', $secret_key );
440
-
441
- // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning.
442
- $iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );
443
-
444
- $plaintext = openssl_decrypt( base64_decode( $ciphertext_base64 ), $encrypt_method, $key, 0, $iv );
445
-
446
- return $plaintext;
447
-
448
- }
449
-
450
- /**
451
- * Decrypt the encrypted string - Fallback.
452
- *
453
- * @param string $ciphertext_base64 - encrypted string.
454
- * @return string
455
- * @since 2.6.3
456
- */
457
- public function decryptString_fallback( $ciphertext_base64 ) {
458
-
459
- $ciphertext_dec = base64_decode( $ciphertext_base64 );
460
- $iv_size = mcrypt_get_iv_size( MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC );
461
- $iv_dec = substr( $ciphertext_dec, 0, $iv_size );
462
- $ciphertext_dec = substr( $ciphertext_dec, $iv_size );
463
- $key = $this->truncateKey();
464
- $plaintext_dec = mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec );
465
- return rtrim( $plaintext_dec, "\0" );
466
-
467
- }
468
-
469
- /**
470
- * Mirroring Occurrences and Metadata Tables.
471
- * Read from current DB and copy into Mirroring DB.
472
- * @param array $args archive Database and limit by date
473
- */
474
- public function MirroringAlertsToDB($args)
475
- {
476
- $last_created_on = null;
477
- $first_occurrence_id = null;
478
- $_wpdb = $this->getConnection();
479
- $mirroring_db = $args['mirroring_db'];
480
-
481
- // Load data Occurrences from WP
482
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($_wpdb);
483
- if (!$occurrence->IsInstalled()) {
484
- return null;
485
- }
486
-
487
- $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' WHERE created_on > ' . $args['last_created_on'];
488
- $occurrences = $_wpdb->get_results($sql, ARRAY_A);
489
-
490
- if (!empty($occurrences)) {
491
- $occurrenceNew = new WSAL_Adapters_MySQL_Occurrence($mirroring_db);
492
-
493
- $sql = 'INSERT INTO ' . $occurrenceNew->GetTable() . ' (id, site_id, alert_id, created_on, is_read) VALUES ' ;
494
- foreach ($occurrences as $entry) {
495
- $sql .= '('.$entry['id'].', '.$entry['site_id'].', '.$entry['alert_id'].', '.$entry['created_on'].', '.$entry['is_read'].'), ';
496
- $last_created_on = $entry['created_on'];
497
- // save the first id
498
- if (empty($first_occurrence_id)) {
499
- $first_occurrence_id = $entry['id'];
500
- }
501
- }
502
- $sql = rtrim($sql, ", ");
503
- $mirroring_db->query($sql);
504
- }
505
-
506
- // Load data Meta from WP
507
- $meta = new WSAL_Adapters_MySQL_Meta($_wpdb);
508
- if (!$meta->IsInstalled()) {
509
- return null;
510
- }
511
- if (!empty($first_occurrence_id)) {
512
- $sql = 'SELECT * FROM ' . $meta->GetTable() . ' WHERE occurrence_id >= ' . $first_occurrence_id;
513
- $metadata = $_wpdb->get_results($sql, ARRAY_A);
514
-
515
- if (!empty($metadata)) {
516
- $metaNew = new WSAL_Adapters_MySQL_Meta($mirroring_db);
517
-
518
- $sql = 'INSERT INTO ' . $metaNew->GetTable() . ' (occurrence_id, name, value) VALUES ' ;
519
- foreach ($metadata as $entry) {
520
- $sql .= '('.$entry['occurrence_id'].', \''.$entry['name'].'\', \''.str_replace(array("'", "\'"), "\'", $entry['value']).'\'), ';
521
- }
522
- $sql = rtrim($sql, ", ");
523
- $mirroring_db->query($sql);
524
- }
525
- }
526
- return $last_created_on;
527
- }
528
-
529
- /**
530
- * Archiving Occurrences Table.
531
- * Read from current DB and copy into Archive DB.
532
- * @param array $args archive Database and limit by count OR by date
533
- */
534
- public function ArchiveOccurrence($args)
535
- {
536
- $_wpdb = $this->getConnection();
537
- $archive_db = $args['archive_db'];
538
-
539
- // Load data Occurrences from WP
540
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($_wpdb);
541
- if (!$occurrence->IsInstalled()) {
542
- return null;
543
- }
544
- if (!empty($args['by_date'])) {
545
- $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' WHERE created_on <= ' . $args['by_date'];
546
- }
547
-
548
- if (!empty($args['by_limit'])) {
549
- $sql = 'SELECT occ.* FROM ' . $occurrence->GetTable() . ' occ
550
- LEFT JOIN (SELECT id FROM ' . $occurrence->GetTable() . ' order by created_on DESC limit ' . $args['by_limit'] . ') as ids
551
- on ids.id = occ.id
552
- WHERE ids.id IS NULL';
553
- }
554
- if (!empty($args['last_created_on'])) {
555
- $sql .= ' AND created_on > ' . $args['last_created_on'];
556
- }
557
- $sql .= ' ORDER BY created_on ASC';
558
- if (!empty($args['limit'])) {
559
- $sql .= ' LIMIT ' . $args['limit'];
560
- }
561
- $occurrences = $_wpdb->get_results($sql, ARRAY_A);
562
-
563
- // Insert data to Archive DB
564
- if (!empty($occurrences)) {
565
- $last = end($occurrences);
566
- $args['last_created_on'] = $last['created_on'];
567
- $args['occurence_ids'] = array();
568
-
569
- $occurrenceNew = new WSAL_Adapters_MySQL_Occurrence($archive_db);
570
-
571
- $sql = 'INSERT INTO ' . $occurrenceNew->GetTable() . ' (id, site_id, alert_id, created_on, is_read) VALUES ' ;
572
- foreach ($occurrences as $entry) {
573
- $sql .= '('.$entry['id'].', '.$entry['site_id'].', '.$entry['alert_id'].', '.$entry['created_on'].', '.$entry['is_read'].'), ';
574
- $args['occurence_ids'][] = $entry['id'];
575
- }
576
- $sql = rtrim($sql, ", ");
577
- $archive_db->query($sql);
578
- return $args;
579
- } else {
580
- return false;
581
- }
582
- }
583
-
584
- /**
585
- * Archiving Metadata Table.
586
- * Read from current DB and copy into Archive DB.
587
- * @param array $args archive Database and occurrences IDs
588
- */
589
- public function ArchiveMeta($args)
590
- {
591
- $_wpdb = $this->getConnection();
592
- $archive_db = $args['archive_db'];
593
-
594
- // Load data Meta from WP
595
- $meta = new WSAL_Adapters_MySQL_Meta($_wpdb);
596
- if (!$meta->IsInstalled()) {
597
- return null;
598
- }
599
- $sOccurenceIds = implode(", ", $args["occurence_ids"]);
600
- $sql = 'SELECT * FROM ' . $meta->GetTable() . ' WHERE occurrence_id IN (' . $sOccurenceIds . ')';
601
- $metadata = $_wpdb->get_results($sql, ARRAY_A);
602
-
603
- // Insert data to Archive DB
604
- if (!empty($metadata)) {
605
- $metaNew = new WSAL_Adapters_MySQL_Meta($archive_db);
606
-
607
- $sql = 'INSERT INTO ' . $metaNew->GetTable() . ' (occurrence_id, name, value) VALUES ' ;
608
- foreach ($metadata as $entry) {
609
- $sql .= '('.$entry['occurrence_id'].', \''.$entry['name'].'\', \''.str_replace(array("'", "\'"), "\'", $entry['value']).'\'), ';
610
- }
611
- $sql = rtrim($sql, ", ");
612
- $archive_db->query($sql);
613
- return $args;
614
- } else {
615
- return false;
616
- }
617
- }
618
-
619
- /**
620
- * Delete Occurrences and Metadata after archiving.
621
- * @param array $args archive Database and occurrences IDs
622
- */
623
- public function DeleteAfterArchive($args)
624
- {
625
- $_wpdb = $this->getConnection();
626
- $archive_db = $args['archive_db'];
627
-
628
- $sOccurenceIds = implode(", ", $args["occurence_ids"]);
629
-
630
- $occurrence = new WSAL_Adapters_MySQL_Occurrence($_wpdb);
631
- $sql = 'DELETE FROM ' . $occurrence->GetTable() . ' WHERE id IN (' . $sOccurenceIds . ')';
632
- $_wpdb->query($sql);
633
-
634
- $meta = new WSAL_Adapters_MySQL_Meta($_wpdb);
635
- $sql = 'DELETE FROM ' . $meta->GetTable() . ' WHERE occurrence_id IN (' . $sOccurenceIds . ')';
636
- $_wpdb->query($sql);
637
- }
638
-
639
- /**
640
- * Truncate string longer than 32 characters.
641
- * Authentication Unique Key @see wp-config.php
642
- * @return string AUTH_KEY
643
- */
644
- private function truncateKey()
645
- {
646
- if (!defined('AUTH_KEY')) {
647
- return 'x4>Tg@G-Kr6a]o-eJeP^?UO)KW;LbV)I';
648
- }
649
- $key_size = strlen(AUTH_KEY);
650
- if ($key_size > 32) {
651
- return substr(AUTH_KEY, 0, 32);
652
- } else {
653
- return AUTH_KEY;
654
- }
655
- }
656
-
657
- /**
658
- * Get OpenSSL IV for DB.
659
- *
660
- * @since 2.6.3
661
- */
662
- private function get_openssl_iv() {
663
-
664
- $secret_openssl_iv = 'і-(аэ┤#≥и┴зейН';
665
- $key_size = strlen( $secret_openssl_iv );
666
- if ( $key_size > 32 ) {
667
- return substr( $secret_openssl_iv, 0, 32 );
668
- } else {
669
- return $secret_openssl_iv;
670
- }
671
-
672
- }
673
  }
1
  <?php
2
  /**
3
+ * Class: MySQL DB Connector.
4
+ *
5
+ * MySQL Connector Class
6
+ * It uses wpdb WordPress DB Class
7
+ *
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * MySQL Connector Class
18
  * It uses wpdb WordPress DB Class
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Connector_MySQLDB extends WSAL_Connector_AbstractConnector implements WSAL_Connector_ConnectorInterface {
23
+
24
+ /**
25
+ * Connection Configuration.
26
+ *
27
+ * @var array
28
+ */
29
+ protected $connectionConfig = null;
30
+
31
+ /**
32
+ * Method: Constructor.
33
+ *
34
+ * @param array $connection_config - Connection config.
35
+ */
36
+ public function __construct( $connection_config = null ) {
37
+ $this->connectionConfig = $connection_config;
38
+ parent::__construct( 'MySQL' );
39
+ require_once( $this->getAdaptersDirectory() . '/OptionAdapter.php' );
40
+ }
41
+
42
+ /**
43
+ * Test the connection.
44
+ *
45
+ * @throws Exception - Connection failed.
46
+ */
47
+ public function TestConnection() {
48
+ error_reporting( E_ALL ^ (E_NOTICE | E_WARNING | E_DEPRECATED) );
49
+ $connection_config = $this->connectionConfig;
50
+ $password = $this->decryptString( $connection_config['password'] );
51
+ $new_wpdb = new wpdbCustom( $connection_config['user'], $password, $connection_config['name'], $connection_config['hostname'] );
52
+
53
+ // Database Error.
54
+ if ( ! $new_wpdb->has_connected ) {
55
+ throw new Exception( 'Connection failed. Please check your connection details.' );
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Creates a connection and returns it
61
+ *
62
+ * @return Instance of WPDB
63
+ */
64
+ private function createConnection() {
65
+ if ( ! empty( $this->connectionConfig ) ) {
66
+ // TO DO: Use the provided connection config.
67
+ $connection_config = $this->connectionConfig;
68
+ $password = $this->decryptString( $connection_config['password'] );
69
+ $new_wpdb = new wpdb( $connection_config['user'], $password, $connection_config['name'], $connection_config['hostname'] );
70
+ $new_wpdb->set_prefix( $connection_config['base_prefix'] );
71
+ return $new_wpdb;
72
+ } else {
73
+ global $wpdb;
74
+ return $wpdb;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Returns a wpdb instance
80
+ *
81
+ * @return wpdb
82
+ */
83
+ public function getConnection() {
84
+ if ( ! empty( $this->connection ) ) {
85
+ return $this->connection;
86
+ } else {
87
+ $this->connection = $this->createConnection();
88
+ return $this->connection;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Close DB connection
94
+ */
95
+ public function closeConnection() {
96
+ $current_wpdb = $this->getConnection();
97
+ $result = $current_wpdb->close();
98
+ return $result;
99
+ }
100
+
101
+ /**
102
+ * Gets an adapter for the specified model.
103
+ *
104
+ * @param string $class_name - Class name.
105
+ * @return WSAL_Adapters_MySQL_{class_name}
106
+ */
107
+ public function getAdapter( $class_name ) {
108
+ $obj_name = $this->getAdapterClassName( $class_name );
109
+ return new $obj_name( $this->getConnection() );
110
+ }
111
+
112
+ /**
113
+ * Gets an adapter class name for the specified model.
114
+ *
115
+ * @param string $class_name - Class name.
116
+ * @return WSAL_Adapters_MySQL_{class_name}
117
+ */
118
+ protected function getAdapterClassName( $class_name ) {
119
+ return 'WSAL_Adapters_MySQL_' . $class_name;
120
+ }
121
+
122
+ /**
123
+ * Checks if the necessary tables are available
124
+ *
125
+ * @return bool true|false
126
+ */
127
+ public function isInstalled() {
128
+ global $wpdb;
129
+ $table = $wpdb->base_prefix . 'wsal_occurrences';
130
+ return ($wpdb->get_var( 'SHOW TABLES LIKE "' . $table . '"' ) == $table);
131
+ }
132
+
133
+ /**
134
+ * Checks if old version tables are available
135
+ *
136
+ * @return bool true|false
137
+ */
138
+ public function canMigrate() {
139
+ $wpdb = $this->getConnection();
140
+ $table = $wpdb->base_prefix . 'wordpress_auditlog_events';
141
+ return ($wpdb->get_var( 'SHOW TABLES LIKE "' . $table . '"' ) == $table);
142
+ }
143
+
144
+ /**
145
+ * Install all DB tables.
146
+ *
147
+ * @param bool $exclude_options - True if excluding.
148
+ */
149
+ public function installAll( $exclude_options = false ) {
150
+ $plugin = WpSecurityAuditLog::GetInstance();
151
+
152
+ foreach ( glob( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . '*.php' ) as $file ) {
153
+ $file_path = explode( DIRECTORY_SEPARATOR, $file );
154
+ $file_name = $file_path[ count( $file_path ) - 1 ];
155
+ $class_name = $this->getAdapterClassName( str_replace( 'Adapter.php', '', $file_name ) );
156
+
157
+ $class = new $class_name( $this->getConnection() );
158
+ if ( $exclude_options && $class instanceof WSAL_Adapters_MySQL_Option ) {
159
+ continue;
160
+ }
161
+
162
+ // Exclude the tmp_users table.
163
+ if ( ! $exclude_options && $class instanceof WSAL_Adapters_MySQL_TmpUser ) {
164
+ continue;
165
+ }
166
+
167
+ if ( is_subclass_of( $class, 'WSAL_Adapters_MySQL_ActiveRecord' ) ) {
168
+ $class->Install();
169
+ }
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Uninstall all DB tables.
175
+ */
176
+ public function uninstallAll() {
177
+ $plugin = WpSecurityAuditLog::GetInstance();
178
+
179
+ foreach ( glob( $this->getAdaptersDirectory() . DIRECTORY_SEPARATOR . '*.php' ) as $file ) {
180
+ $file_path = explode( DIRECTORY_SEPARATOR, $file );
181
+ $file_name = $file_path[ count( $file_path ) - 1 ];
182
+ $class_name = $this->getAdapterClassName( str_replace( 'Adapter.php', '', $file_name ) );
183
+
184
+ $class = new $class_name( $this->getConnection() );
185
+ if ( is_subclass_of( $class, 'WSAL_Adapters_MySQL_ActiveRecord' ) ) {
186
+ $class->Uninstall();
187
+ }
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Increase occurrence ID
193
+ *
194
+ * @return integer MAX(id)
195
+ */
196
+ private function GetIncreaseOccurrence() {
197
+ $_wpdb = $this->getConnection();
198
+ $occurrence_new = new WSAL_Adapters_MySQL_Occurrence( $_wpdb );
199
+ $sql = 'SELECT MAX(id) FROM ' . $occurrence_new->GetTable();
200
+ return (int) $_wpdb->get_var( $sql );
201
+ }
202
+
203
+ /**
204
+ * Migrate Metadata from WP DB to External DB.
205
+ *
206
+ * @param integer $index - Index.
207
+ * @param integer $limit - Limit.
208
+ */
209
+ public function MigrateMeta( $index, $limit ) {
210
+ $result = null;
211
+ $offset = ($index * $limit);
212
+ global $wpdb;
213
+ $_wpdb = $this->getConnection();
214
+ // Add +1 because an alert is generated after delete the metadata table.
215
+ $increase_occurrence_id = $this->GetIncreaseOccurrence() + 1;
216
+
217
+ // Load data Meta from WP.
218
+ $meta = new WSAL_Adapters_MySQL_Meta( $wpdb );
219
+ if ( ! $meta->IsInstalled() ) {
220
+ $result['empty'] = true;
221
+ return $result;
222
+ }
223
+ $sql = 'SELECT * FROM ' . $meta->GetWPTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
224
+ $metadata = $wpdb->get_results( $sql, ARRAY_A );
225
+
226
+ // Insert data to External DB.
227
+ if ( ! empty( $metadata ) ) {
228
+ $meta_new = new WSAL_Adapters_MySQL_Meta( $_wpdb );
229
+
230
+ $index++;
231
+ $sql = 'INSERT INTO ' . $meta_new->GetTable() . ' (occurrence_id, name, value) VALUES ' ;
232
+ foreach ( $metadata as $entry ) {
233
+ $occurrence_id = intval( $entry['occurrence_id'] ) + $increase_occurrence_id;
234
+ $sql .= '(' . $occurrence_id . ', \'' . $entry['name'] . '\', \'' . str_replace( array( "'", "\'" ), "\'", $entry['value'] ) . '\'), ';
235
+ }
236
+ $sql = rtrim( $sql, ', ' );
237
+ $_wpdb->query( $sql );
238
+
239
+ $result['complete'] = false;
240
+ } else {
241
+ $result['complete'] = true;
242
+ $this->DeleteAfterMigrate( $meta );
243
+ }
244
+ $result['index'] = $index;
245
+ return $result;
246
+ }
247
+
248
+ /**
249
+ * Migrate Occurrences from WP DB to External DB.
250
+ *
251
+ * @param integer $index - Index.
252
+ * @param integer $limit - Limit.
253
+ */
254
+ public function MigrateOccurrence( $index, $limit ) {
255
+ $result = null;
256
+ $offset = ($index * $limit);
257
+ global $wpdb;
258
+ $_wpdb = $this->getConnection();
259
+
260
+ // Load data Occurrences from WP.
261
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $wpdb );
262
+ if ( ! $occurrence->IsInstalled() ) {
263
+ $result['empty'] = true;
264
+ return $result;
265
+ }
266
+ $sql = 'SELECT * FROM ' . $occurrence->GetWPTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
267
+ $occurrences = $wpdb->get_results( $sql, ARRAY_A );
268
+
269
+ // Insert data to External DB.
270
+ if ( ! empty( $occurrences ) ) {
271
+ $occurrence_new = new WSAL_Adapters_MySQL_Occurrence( $_wpdb );
272
+
273
+ $index++;
274
+ $sql = 'INSERT INTO ' . $occurrence_new->GetTable() . ' (site_id, alert_id, created_on, is_read) VALUES ' ;
275
+ foreach ( $occurrences as $entry ) {
276
+ $sql .= '(' . $entry['site_id'] . ', ' . $entry['alert_id'] . ', ' . $entry['created_on'] . ', ' . $entry['is_read'] . '), ';
277
+ }
278
+ $sql = rtrim( $sql, ', ' );
279
+ $_wpdb->query( $sql );
280
+
281
+ $result['complete'] = false;
282
+ } else {
283
+ $result['complete'] = true;
284
+ $this->DeleteAfterMigrate( $occurrence );
285
+ }
286
+ $result['index'] = $index;
287
+ return $result;
288
+ }
289
+
290
+ /**
291
+ * Migrate Back Occurrences from External DB to WP DB.
292
+ *
293
+ * @param integer $index - Index.
294
+ * @param integer $limit - Limit.
295
+ */
296
+ public function MigrateBackOccurrence( $index, $limit ) {
297
+ $result = null;
298
+ $offset = ($index * $limit);
299
+ global $wpdb;
300
+ $_wpdb = $this->getConnection();
301
+
302
+ // Load data Occurrences from External DB.
303
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $_wpdb );
304
+ if ( ! $occurrence->IsInstalled() ) {
305
+ $result['empty'] = true;
306
+ return $result;
307
+ }
308
+ $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
309
+ $occurrences = $_wpdb->get_results( $sql, ARRAY_A );
310
+
311
+ // Insert data to WP.
312
+ if ( ! empty( $occurrences ) ) {
313
+ $occurrence_wp = new WSAL_Adapters_MySQL_Occurrence( $wpdb );
314
+
315
+ $index++;
316
+ $sql = 'INSERT INTO ' . $occurrence_wp->GetWPTable() . ' (id, site_id, alert_id, created_on, is_read) VALUES ' ;
317
+ foreach ( $occurrences as $entry ) {
318
+ $sql .= '(' . $entry['id'] . ', ' . $entry['site_id'] . ', ' . $entry['alert_id'] . ', ' . $entry['created_on'] . ', ' . $entry['is_read'] . '), ';
319
+ }
320
+ $sql = rtrim( $sql, ', ' );
321
+ $wpdb->query( $sql );
322
+
323
+ $result['complete'] = false;
324
+ } else {
325
+ $result['complete'] = true;
326
+ }
327
+ $result['index'] = $index;
328
+ return $result;
329
+ }
330
+
331
+ /**
332
+ * Migrate Back Metadata from External DB to WP DB.
333
+ *
334
+ * @param integer $index - Index.
335
+ * @param integer $limit - Limit.
336
+ */
337
+ public function MigrateBackMeta( $index, $limit ) {
338
+ $result = null;
339
+ $offset = ($index * $limit);
340
+ global $wpdb;
341
+ $_wpdb = $this->getConnection();
342
+
343
+ // Load data Meta from External DB.
344
+ $meta = new WSAL_Adapters_MySQL_Meta( $_wpdb );
345
+ if ( ! $meta->IsInstalled() ) {
346
+ $result['empty'] = true;
347
+ return $result;
348
+ }
349
+ $sql = 'SELECT * FROM ' . $meta->GetTable() . ' LIMIT ' . $limit . ' OFFSET ' . $offset;
350
+ $metadata = $_wpdb->get_results( $sql, ARRAY_A );
351
+
352
+ // Insert data to WP.
353
+ if ( ! empty( $metadata ) ) {
354
+ $meta_wp = new WSAL_Adapters_MySQL_Meta( $wpdb );
355
+
356
+ $index++;
357
+ $sql = 'INSERT INTO ' . $meta_wp->GetWPTable() . ' (occurrence_id, name, value) VALUES ' ;
358
+ foreach ( $metadata as $entry ) {
359
+ $sql .= '(' . $entry['occurrence_id'] . ', \'' . $entry['name'] . '\', \'' . str_replace( array( "'", "\'" ), "\'", $entry['value'] ) . '\'), ';
360
+ }
361
+ $sql = rtrim( $sql, ', ' );
362
+ $wpdb->query( $sql );
363
+
364
+ $result['complete'] = false;
365
+ } else {
366
+ $result['complete'] = true;
367
+ }
368
+ $result['index'] = $index;
369
+ return $result;
370
+ }
371
+
372
+ /**
373
+ * Delete after Migrate alerts.
374
+ *
375
+ * @param object $record - Type of record.
376
+ */
377
+ private function DeleteAfterMigrate( $record ) {
378
+ global $wpdb;
379
+ $sql = 'DROP TABLE IF EXISTS ' . $record->GetTable();
380
+ $wpdb->query( $sql );
381
+ }
382
+
383
+ /**
384
+ * Encrypt plain text.
385
+ * Encrypt string, before saves it to the DB.
386
+ *
387
+ * @param string $plaintext - Plain text that is going to be encrypted.
388
+ * @return string
389
+ * @since 2.6.3
390
+ */
391
+ public function encryptString( $plaintext ) {
392
+ // Check for previous version.
393
+ $plugin = WpSecurityAuditLog::GetInstance();
394
+ $version = $plugin->GetGlobalOption( 'version', '0.0.0' );
395
+
396
+ if ( -1 === version_compare( $version, '2.6.2' ) ) {
397
+ return $this->encryptString_fallback( $plaintext );
398
+ }
399
+
400
+ $ciphertext = false;
401
+
402
+ $encrypt_method = 'AES-256-CBC';
403
+ $secret_key = $this->truncateKey();
404
+ $secret_iv = $this->get_openssl_iv();
405
+
406
+ // Hash the key.
407
+ $key = hash( 'sha256', $secret_key );
408
+
409
+ // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning.
410
+ $iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );
411
+
412
+ $ciphertext = openssl_encrypt( $plaintext, $encrypt_method, $key, 0, $iv );
413
+ $ciphertext = base64_encode( $ciphertext );
414
+
415
+ return $ciphertext;
416
+ }
417
+
418
+ /**
419
+ * Encrypt plain text - Fallback.
420
+ *
421
+ * @param string $plaintext - Plain text that is going to be encrypted.
422
+ * @return string
423
+ * @since 2.6.3
424
+ */
425
+ public function encryptString_fallback( $plaintext ) {
426
+ $iv_size = mcrypt_get_iv_size( MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC );
427
+ $iv = mcrypt_create_iv( $iv_size, MCRYPT_RAND );
428
+ $key = $this->truncateKey();
429
+ $ciphertext = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC, $iv );
430
+ $ciphertext = $iv . $ciphertext;
431
+ $ciphertext_base64 = base64_encode( $ciphertext );
432
+ return $ciphertext_base64;
433
+ }
434
+
435
+ /**
436
+ * Decrypt the encrypted string.
437
+ * Decrypt string, after reads it from the DB.
438
+ *
439
+ * @param string $ciphertext_base64 - encrypted string.
440
+ * @return string
441
+ * @since 2.6.3
442
+ */
443
+ public function decryptString( $ciphertext_base64 ) {
444
+ // Check for previous version.
445
+ $plugin = WpSecurityAuditLog::GetInstance();
446
+ $version = $plugin->GetGlobalOption( 'version', '0.0.0' );
447
+
448
+ if ( -1 === version_compare( $version, '2.6.2' ) ) {
449
+ return $this->decryptString_fallback( $ciphertext_base64 );
450
+ }
451
+
452
+ $plaintext = false;
453
+
454
+ $encrypt_method = 'AES-256-CBC';
455
+ $secret_key = $this->truncateKey();
456
+ $secret_iv = $this->get_openssl_iv();
457
+
458
+ // Hash the key.
459
+ $key = hash( 'sha256', $secret_key );
460
+
461
+ // iv - encrypt method AES-256-CBC expects 16 bytes - else you will get a warning.
462
+ $iv = substr( hash( 'sha256', $secret_iv ), 0, 16 );
463
+
464
+ $plaintext = openssl_decrypt( base64_decode( $ciphertext_base64 ), $encrypt_method, $key, 0, $iv );
465
+
466
+ return $plaintext;
467
+ }
468
+
469
+ /**
470
+ * Decrypt the encrypted string - Fallback.
471
+ *
472
+ * @param string $ciphertext_base64 - encrypted string.
473
+ * @return string
474
+ * @since 2.6.3
475
+ */
476
+ public function decryptString_fallback( $ciphertext_base64 ) {
477
+ $ciphertext_dec = base64_decode( $ciphertext_base64 );
478
+ $iv_size = mcrypt_get_iv_size( MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC );
479
+ $iv_dec = substr( $ciphertext_dec, 0, $iv_size );
480
+ $ciphertext_dec = substr( $ciphertext_dec, $iv_size );
481
+ $key = $this->truncateKey();
482
+ $plaintext_dec = mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec );
483
+ return rtrim( $plaintext_dec, "\0" );
484
+ }
485
+
486
+ /**
487
+ * Mirroring Occurrences and Metadata Tables.
488
+ * Read from current DB and copy into Mirroring DB.
489
+ *
490
+ * @param array $args - Archive Database and limit by date.
491
+ */
492
+ public function MirroringAlertsToDB( $args ) {
493
+ $last_created_on = null;
494
+ $first_occurrence_id = null;
495
+ $_wpdb = $this->getConnection();
496
+ $mirroring_db = $args['mirroring_db'];
497
+
498
+ // Load data Occurrences from WP.
499
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $_wpdb );
500
+ if ( ! $occurrence->IsInstalled() ) {
501
+ return null;
502
+ }
503
+
504
+ $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' WHERE created_on > ' . $args['last_created_on'];
505
+ $occurrences = $_wpdb->get_results( $sql, ARRAY_A );
506
+
507
+ if ( ! empty( $occurrences ) ) {
508
+ $occurrence_new = new WSAL_Adapters_MySQL_Occurrence( $mirroring_db );
509
+
510
+ $sql = 'INSERT INTO ' . $occurrence_new->GetTable() . ' (id, site_id, alert_id, created_on, is_read) VALUES ' ;
511
+ foreach ( $occurrences as $entry ) {
512
+ $sql .= '(' . $entry['id'] . ', ' . $entry['site_id'] . ', ' . $entry['alert_id'] . ', ' . $entry['created_on'] . ', ' . $entry['is_read'] . '), ';
513
+ $last_created_on = $entry['created_on'];
514
+ // Save the first id.
515
+ if ( empty( $first_occurrence_id ) ) {
516
+ $first_occurrence_id = $entry['id'];
517
+ }
518
+ }
519
+ $sql = rtrim( $sql, ', ' );
520
+ $mirroring_db->query( $sql );
521
+ }
522
+
523
+ // Load data Meta from WP.
524
+ $meta = new WSAL_Adapters_MySQL_Meta( $_wpdb );
525
+ if ( ! $meta->IsInstalled() ) {
526
+ return null;
527
+ }
528
+ if ( ! empty( $first_occurrence_id ) ) {
529
+ $sql = 'SELECT * FROM ' . $meta->GetTable() . ' WHERE occurrence_id >= ' . $first_occurrence_id;
530
+ $metadata = $_wpdb->get_results( $sql, ARRAY_A );
531
+
532
+ if ( ! empty( $metadata ) ) {
533
+ $meta_new = new WSAL_Adapters_MySQL_Meta( $mirroring_db );
534
+
535
+ $sql = 'INSERT INTO ' . $meta_new->GetTable() . ' (occurrence_id, name, value) VALUES ' ;
536
+ foreach ( $metadata as $entry ) {
537
+ $sql .= '(' . $entry['occurrence_id'] . ', \'' . $entry['name'] . '\', \'' . str_replace( array( "'", "\'" ), "\'", $entry['value'] ) . '\'), ';
538
+ }
539
+ $sql = rtrim( $sql, ', ' );
540
+ $mirroring_db->query( $sql );
541
+ }
542
+ }
543
+ return $last_created_on;
544
+ }
545
+
546
+ /**
547
+ * Archiving Occurrences Table.
548
+ * Read from current DB and copy into Archive DB.
549
+ *
550
+ * @param array $args - Archive Database and limit by count OR by date.
551
+ */
552
+ public function ArchiveOccurrence( $args ) {
553
+ $_wpdb = $this->getConnection();
554
+ $archive_db = $args['archive_db'];
555
+
556
+ // Load data Occurrences from WP.
557
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $_wpdb );
558
+ if ( ! $occurrence->IsInstalled() ) {
559
+ return null;
560
+ }
561
+ if ( ! empty( $args['by_date'] ) ) {
562
+ $sql = 'SELECT * FROM ' . $occurrence->GetTable() . ' WHERE created_on <= ' . $args['by_date'];
563
+ }
564
+
565
+ if ( ! empty( $args['by_limit'] ) ) {
566
+ $sql = 'SELECT occ.* FROM ' . $occurrence->GetTable() . ' occ
567
+ LEFT JOIN (SELECT id FROM ' . $occurrence->GetTable() . ' order by created_on DESC limit ' . $args['by_limit'] . ') as ids
568
+ on ids.id = occ.id
569
+ WHERE ids.id IS NULL';
570
+ }
571
+ if ( ! empty( $args['last_created_on'] ) ) {
572
+ $sql .= ' AND created_on > ' . $args['last_created_on'];
573
+ }
574
+ $sql .= ' ORDER BY created_on ASC';
575
+ if ( ! empty( $args['limit'] ) ) {
576
+ $sql .= ' LIMIT ' . $args['limit'];
577
+ }
578
+ $occurrences = $_wpdb->get_results( $sql, ARRAY_A );
579
+
580
+ // Insert data to Archive DB.
581
+ if ( ! empty( $occurrences ) ) {
582
+ $last = end( $occurrences );
583
+ $args['last_created_on'] = $last['created_on'];
584
+ $args['occurence_ids'] = array();
585
+
586
+ $occurrence_new = new WSAL_Adapters_MySQL_Occurrence( $archive_db );
587
+
588
+ $sql = 'INSERT INTO ' . $occurrence_new->GetTable() . ' (id, site_id, alert_id, created_on, is_read) VALUES ' ;
589
+ foreach ( $occurrences as $entry ) {
590
+ $sql .= '(' . $entry['id'] . ', ' . $entry['site_id'] . ', ' . $entry['alert_id'] . ', ' . $entry['created_on'] . ', ' . $entry['is_read'] . '), ';
591
+ $args['occurence_ids'][] = $entry['id'];
592
+ }
593
+ $sql = rtrim( $sql, ', ' );
594
+ $archive_db->query( $sql );
595
+ return $args;
596
+ } else {
597
+ return false;
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Archiving Metadata Table.
603
+ * Read from current DB and copy into Archive DB.
604
+ *
605
+ * @param array $args - Archive Database and occurrences IDs.
606
+ */
607
+ public function ArchiveMeta( $args ) {
608
+ $_wpdb = $this->getConnection();
609
+ $archive_db = $args['archive_db'];
610
+
611
+ // Load data Meta from WP.
612
+ $meta = new WSAL_Adapters_MySQL_Meta( $_wpdb );
613
+ if ( ! $meta->IsInstalled() ) {
614
+ return null;
615
+ }
616
+ $s_occurence_ids = implode( ', ', $args['occurence_ids'] );
617
+ $sql = 'SELECT * FROM ' . $meta->GetTable() . ' WHERE occurrence_id IN (' . $s_occurence_ids . ')';
618
+ $metadata = $_wpdb->get_results( $sql, ARRAY_A );
619
+
620
+ // Insert data to Archive DB.
621
+ if ( ! empty( $metadata ) ) {
622
+ $meta_new = new WSAL_Adapters_MySQL_Meta( $archive_db );
623
+
624
+ $sql = 'INSERT INTO ' . $meta_new->GetTable() . ' (occurrence_id, name, value) VALUES ' ;
625
+ foreach ( $metadata as $entry ) {
626
+ $sql .= '(' . $entry['occurrence_id'] . ', \'' . $entry['name'] . '\', \'' . str_replace( array( "'", "\'" ), "\'", $entry['value'] ) . '\'), ';
627
+ }
628
+ $sql = rtrim( $sql, ', ' );
629
+ $archive_db->query( $sql );
630
+ return $args;
631
+ } else {
632
+ return false;
633
+ }
634
+ }
635
+
636
+ /**
637
+ * Delete Occurrences and Metadata after archiving.
638
+ *
639
+ * @param array $args - Archive Database and occurrences IDs.
640
+ */
641
+ public function DeleteAfterArchive( $args ) {
642
+ $_wpdb = $this->getConnection();
643
+ $archive_db = $args['archive_db'];
644
+
645
+ $s_occurence_ids = implode( ', ', $args['occurence_ids'] );
646
+
647
+ $occurrence = new WSAL_Adapters_MySQL_Occurrence( $_wpdb );
648
+ $sql = 'DELETE FROM ' . $occurrence->GetTable() . ' WHERE id IN (' . $s_occurence_ids . ')';
649
+ $_wpdb->query( $sql );
650
+
651
+ $meta = new WSAL_Adapters_MySQL_Meta( $_wpdb );
652
+ $sql = 'DELETE FROM ' . $meta->GetTable() . ' WHERE occurrence_id IN (' . $s_occurence_ids . ')';
653
+ $_wpdb->query( $sql );
654
+ }
655
+
656
+ /**
657
+ * Truncate string longer than 32 characters.
658
+ * Authentication Unique Key @see wp-config.php
659
+ *
660
+ * @return string AUTH_KEY
661
+ */
662
+ private function truncateKey() {
663
+ if ( ! defined( 'AUTH_KEY' ) ) {
664
+ return 'x4>Tg@G-Kr6a]o-eJeP^?UO)KW;LbV)I';
665
+ }
666
+ $key_size = strlen( AUTH_KEY );
667
+ if ( $key_size > 32 ) {
668
+ return substr( AUTH_KEY, 0, 32 );
669
+ } else {
670
+ return AUTH_KEY;
671
+ }
672
+ }
673
+
674
+ /**
675
+ * Get OpenSSL IV for DB.
676
+ *
677
+ * @since 2.6.3
678
+ */
679
+ private function get_openssl_iv() {
680
+ $secret_openssl_iv = 'і-(аэ┤#≥и┴зейН';
681
+ $key_size = strlen( $secret_openssl_iv );
682
+ if ( $key_size > 32 ) {
683
+ return substr( $secret_openssl_iv, 0, 32 );
684
+ } else {
685
+ return $secret_openssl_iv;
686
+ }
687
+ }
688
  }
classes/Connector/wp-db-custom.php CHANGED
@@ -1,44 +1,59 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * Test the DB connection.
5
  * It uses wpdb WordPress DB Class.
 
 
6
  */
7
- class wpdbCustom extends wpdb
8
- {
9
- /**
10
- * Overwrite wpdb class for set $allow_bail to false
11
- * and hide the print of the error
12
- * @global string $wp_version
13
- * @param string $dbuser MySQL database user
14
- * @param string $dbpassword MySQL database password
15
- * @param string $dbname MySQL database name
16
- * @param string $dbhost MySQL database host
17
- */
18
- public function __construct($dbuser, $dbpassword, $dbname, $dbhost)
19
- {
20
- register_shutdown_function(array($this, '__destruct'));
21
- if (WP_DEBUG && WP_DEBUG_DISPLAY) {
22
- $this->show_errors();
23
- }
24
- if (function_exists('mysqli_connect')) {
25
- if (defined('WP_USE_EXT_MYSQL')) {
26
- $this->use_mysqli = ! WP_USE_EXT_MYSQL;
27
- } elseif (version_compare(phpversion(), '5.5', '>=') || !function_exists('mysql_connect')) {
28
- $this->use_mysqli = true;
29
- } elseif (false !== strpos($GLOBALS['wp_version'], '-')) {
30
- $this->use_mysqli = true;
31
- }
32
- }
33
- $this->dbuser = $dbuser;
34
- $this->dbpassword = $dbpassword;
35
- $this->dbname = $dbname;
36
- $this->dbhost = $dbhost;
37
- // wp-config.php creation will manually connect when ready.
38
- if (defined('WP_SETUP_CONFIG')) {
39
- return;
40
- }
41
 
42
- $this->db_connect(false);
43
- }
44
  }
1
  <?php
2
  /**
3
+ * Class: Custom DB Class
4
+ *
5
+ * Test the DB connection.
6
+ * It uses wpdb WordPress DB Class.
7
+ *
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Test the DB connection.
18
  * It uses wpdb WordPress DB Class.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class wpdbCustom extends wpdb {
23
+
24
+ /**
25
+ * Overwrite wpdb class for set $allow_bail to false
26
+ * and hide the print of the error
27
+ *
28
+ * @global string $wp_version
29
+ * @param string $dbuser - MySQL database user.
30
+ * @param string $dbpassword - MySQL database password.
31
+ * @param string $dbname - MySQL database name.
32
+ * @param string $dbhost - MySQL database host.
33
+ */
34
+ public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
35
+ register_shutdown_function( array( $this, '__destruct' ) );
36
+ if ( WP_DEBUG && WP_DEBUG_DISPLAY ) {
37
+ $this->show_errors();
38
+ }
39
+ if ( function_exists( 'mysqli_connect' ) ) {
40
+ if ( defined( 'WP_USE_EXT_MYSQL' ) ) {
41
+ $this->use_mysqli = ! WP_USE_EXT_MYSQL;
42
+ } elseif ( version_compare( phpversion(), '5.5', '>=' ) || ! function_exists( 'mysql_connect' ) ) {
43
+ $this->use_mysqli = true;
44
+ } elseif ( false !== strpos( $GLOBALS['wp_version'], '-' ) ) {
45
+ $this->use_mysqli = true;
46
+ }
47
+ }
48
+ $this->dbuser = $dbuser;
49
+ $this->dbpassword = $dbpassword;
50
+ $this->dbname = $dbname;
51
+ $this->dbhost = $dbhost;
52
+ // wp-config.php creation will manually connect when ready.
53
+ if ( defined( 'WP_SETUP_CONFIG' ) ) {
54
+ return;
55
+ }
56
 
57
+ $this->db_connect( false );
58
+ }
59
  }
classes/ConstantManager.php CHANGED
@@ -1,96 +1,104 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
- *
5
  * Class used for Constants,
6
  * E_NOTICE, E_WARNING, E_CRITICAL, etc.
 
 
7
  */
8
- class WSAL_ConstantManager
9
- {
10
- protected $_constants = array();
11
-
12
- /**
13
- * Use an existing PHP constant.
14
- * @param string $name Constant name.
15
- * @param string $description Constant description.
16
- */
17
- public function UseConstant($name, $description = '')
18
- {
19
- $this->_constants[] = (object)array(
20
- 'name' => $name,
21
- 'value' => constant($name),
22
- 'description' => $description,
23
- );
24
- }
25
-
26
- /**
27
- * Add new PHP constant.
28
- * @param string $name Constant name.
29
- * @param integer|string $value Constant value.
30
- * @param string $description Constant description.
31
- */
32
- public function AddConstant($name, $value, $description = '')
33
- {
34
- // check for constant conflict and define new one if required
35
- if (defined($name) && constant($name) !== $value) {
36
- throw new Exception('Constant already defined with a different value.');
37
- } else {
38
- define($name, $value);
39
- }
40
- // add constant to da list.
41
- $this->UseConstant($name, $description);
42
- }
43
-
44
- /**
45
- * Add multiple constants in one go.
46
- * @param array $items Array of arrays with name, value, description pairs.
47
- */
48
- public function AddConstants($items)
49
- {
50
- foreach ($items as $item) {
51
- $this->AddConstant(
52
- $item['name'],
53
- $item['value'],
54
- $item['description']
55
- );
56
- }
57
- }
58
-
59
- /**
60
- * Use multiple constants in one go.
61
- * @param array $items Array of arrays with name, description pairs.
62
- */
63
- public function UseConstants($items)
64
- {
65
- foreach ($items as $item) {
66
- $this->UseConstant(
67
- $item['name'],
68
- $item['description']
69
- );
70
- }
71
- }
72
-
73
- /**
74
- * Get constant details by a particular detail.
75
- * @param string $what The type of detail: 'name', 'value'
76
- * @param mixed $value The detail expected value.
77
- * @return mixed Either constant details (props: name, value, description) or $default if not found.
78
- */
79
- public function GetConstantBy($what, $value, $default = null)
80
- {
81
- // make sure we do have some constants...
82
- if (count($this->_constants)) {
83
- // make sure that constants do have a $what property
84
- if (!isset($this->_constants[0]->$what)) {
85
- throw new Exception('Unexpected detail type "' . $what . '".');
86
- }
87
- // return constant match the property value
88
- foreach ($this->_constants as $constant) {
89
- if ($constant->$what == $value) {
90
- return $constant;
91
- }
92
- }
93
- }
94
- return $default;
95
- }
 
 
 
 
 
 
 
 
96
  }
1
  <?php
2
  /**
 
 
3
  * Class used for Constants,
4
  * E_NOTICE, E_WARNING, E_CRITICAL, etc.
5
+ *
6
+ * @package Wsal
7
  */
8
+ class WSAL_ConstantManager {
9
+
10
+ /**
11
+ * Constants array.
12
+ *
13
+ * @var array
14
+ */
15
+ protected $_constants = array();
16
+
17
+ /**
18
+ * Use an existing PHP constant.
19
+ *
20
+ * @param string $name - Constant name.
21
+ * @param string $description - Constant description.
22
+ */
23
+ public function UseConstant( $name, $description = '' ) {
24
+ $this->_constants[] = (object) array(
25
+ 'name' => $name,
26
+ 'value' => constant( $name ),
27
+ 'description' => $description,
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Add new PHP constant.
33
+ *
34
+ * @param string $name - Constant name.
35
+ * @param integer|string $value - Constant value.
36
+ * @param string $description - Constant description.
37
+ * @throws string - Error if a constant is already defined.
38
+ */
39
+ public function AddConstant( $name, $value, $description = '' ) {
40
+ // Check for constant conflict and define new one if required.
41
+ if ( defined( $name ) && constant( $name ) !== $value ) {
42
+ throw new Exception( 'Constant already defined with a different value.' );
43
+ } else {
44
+ define( $name, $value );
45
+ }
46
+ // Add constant to da list.
47
+ $this->UseConstant( $name, $description );
48
+ }
49
+
50
+ /**
51
+ * Add multiple constants in one go.
52
+ *
53
+ * @param array $items - Array of arrays with name, value, description pairs.
54
+ */
55
+ public function AddConstants( $items ) {
56
+ foreach ( $items as $item ) {
57
+ $this->AddConstant(
58
+ $item['name'],
59
+ $item['value'],
60
+ $item['description']
61
+ );
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Use multiple constants in one go.
67
+ *
68
+ * @param array $items - Array of arrays with name, description pairs.
69
+ */
70
+ public function UseConstants( $items ) {
71
+ foreach ( $items as $item ) {
72
+ $this->UseConstant(
73
+ $item['name'],
74
+ $item['description']
75
+ );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Get constant details by a particular detail.
81
+ *
82
+ * @param string $what - The type of detail: 'name', 'value'.
83
+ * @param mixed $value - The detail expected value.
84
+ * @param mix $default - Default value of constant.
85
+ * @throws string - Error if detail type is unexpected.
86
+ * @return mixed Either constant details (props: name, value, description) or $default if not found.
87
+ */
88
+ public function GetConstantBy( $what, $value, $default = null ) {
89
+ // Make sure we do have some constants.
90
+ if ( count( $this->_constants ) ) {
91
+ // Make sure that constants do have a $what property.
92
+ if ( ! isset( $this->_constants[0]->$what ) ) {
93
+ throw new Exception( 'Unexpected detail type "' . $what . '".' );
94
+ }
95
+ // Return constant match the property value.
96
+ foreach ( $this->_constants as $constant ) {
97
+ if ( $constant->$what == $value ) {
98
+ return $constant;
99
+ }
100
+ }
101
+ }
102
+ return $default;
103
+ }
104
  }
classes/EDD_SL_Plugin_Updater.php CHANGED
@@ -1,173 +1,221 @@
1
  <?php
2
- // uncomment this line for testing
3
- //set_site_transient( 'update_plugins', null );
4
-
5
  /**
 
 
 
 
6
  * @package Wsal
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  * Allows plugins to use their own update API.
8
  *
9
  * @author Pippin Williamson
10
  * @version 1.2
 
11
  */
12
- class EDD_SL_Plugin_Updater
13
- {
14
- private $api_url = '';
15
- private $api_data = array();
16
- private $name = '';
17
- private $slug = '';
18
- private $do_check = false;
19
-
20
- /**
21
- * Class constructor.
22
- *
23
- * @uses plugin_basename()
24
- * @uses hook()
25
- *
26
- * @param string $_api_url The URL pointing to the custom API endpoint.
27
- * @param string $_plugin_file Path to the plugin file.
28
- * @param array $_api_data Optional data to send with API calls.
29
- * @return void
30
- */
31
- public function __construct($_api_url, $_plugin_file, $_api_data = null)
32
- {
33
- $this->api_url = trailingslashit($_api_url);
34
- $this->api_data = urlencode_deep($_api_data);
35
- $this->name = plugin_basename($_plugin_file);
36
- $this->slug = basename($_plugin_file, '.php');
37
- $this->version = $_api_data['version'];
38
-
39
- // Set up hooks.
40
- $this->hook();
41
- }
42
-
43
- /**
44
- * Set up WordPress filters to hook into WP's update process.
45
- *
46
- * @uses add_filter()
47
- * @return void
48
- */
49
- private function hook()
50
- {
51
- add_filter('pre_set_site_transient_update_plugins', array( $this, 'pre_set_site_transient_update_plugins_filter'));
52
- add_filter('plugins_api', array( $this, 'plugins_api_filter' ), 10, 3);
53
- add_filter('http_request_args', array( $this, 'http_request_args' ), 10, 2);
54
- }
55
-
56
- /**
57
- * Check for Updates at the defined API endpoint and modify the update array.
58
- *
59
- * This function dives into the update API just when WordPress creates its update array,
60
- * then adds a custom API call and injects the custom plugin data retrieved from the API.
61
- * It is reassembled from parts of the native WordPress plugin update code.
62
- * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
63
- *
64
- * @uses api_request()
65
- *
66
- * @param array $_transient_data Update array build by WordPress.
67
- * @return array Modified update array with custom plugin data.
68
- */
69
- public function pre_set_site_transient_update_plugins_filter($_transient_data)
70
- {
71
- if (empty($_transient_data) || !$this->do_check) {
72
- // This ensures that the custom API request only runs on the second time that WP fires the update check
73
- $this->do_check = true;
74
- return $_transient_data;
75
- }
76
-
77
- $to_send = array('slug' => $this->slug);
78
- $api_response = $this->api_request('plugin_latest_version', $to_send);
79
-
80
- if (false !== $api_response && is_object($api_response) && isset($api_response->new_version)) {
81
- if (version_compare($this->version, $api_response->new_version, '<')) {
82
- $_transient_data->response[$this->name] = $api_response;
83
- }
84
- }
85
- return $_transient_data;
86
- }
87
-
88
- /**
89
- * Updates information on the "View version x.x details" page with custom data.
90
- *
91
- * @uses api_request()
92
- *
93
- * @param mixed $_data
94
- * @param string $_action
95
- * @param object $_args
96
- * @return object $_data
97
- */
98
- public function plugins_api_filter($_data, $_action = '', $_args = null)
99
- {
100
- if (($_action != 'plugin_information') || !isset($_args->slug) || ($_args->slug != $this->slug)) {
101
- return $_data;
102
- }
103
- $to_send = array('slug' => $this->slug);
104
-
105
- $api_response = $this->api_request('plugin_information', $to_send);
106
- if (false !== $api_response) {
107
- $_data = $api_response;
108
- }
109
-
110
- return $_data;
111
- }
112
-
113
- /**
114
- * Disable SSL verification in order to prevent download update failures
115
- *
116
- * @param array $args
117
- * @param string $url
118
- * @return object $array
119
- */
120
- public function http_request_args($args, $url)
121
- {
122
- // If it is an https request and we are performing a package download, disable ssl verification
123
- if (strpos($url, 'https://') !== false && strpos($url, 'edd_action=package_download')) {
124
- $args['sslverify'] = false;
125
- }
126
- return $args;
127
- }
128
-
129
- /**
130
- * Calls the API and, if successfull, returns the object delivered by the API.
131
- *
132
- * @uses get_bloginfo()
133
- * @uses wp_remote_post()
134
- * @uses is_wp_error()
135
- *
136
- * @param string $_action The requested action.
137
- * @param array $_data Parameters for the API action.
138
- * @return false||object
139
- */
140
- private function api_request($_action, $_data)
141
- {
142
- global $wp_version;
143
- $data = array_merge($this->api_data, $_data);
144
-
145
- if ($data['slug'] != $this->slug) {
146
- return;
147
- }
148
-
149
- if (empty($data['license'])) {
150
- return;
151
- }
152
-
153
- $api_params = array(
154
- 'edd_action' => 'get_version',
155
- 'license' => $data['license'],
156
- 'name' => $data['item_name'],
157
- 'slug' => $this->slug,
158
- 'author' => $data['author'],
159
- 'url' => home_url()
160
- );
161
- $request = wp_remote_post($this->api_url, array('timeout' => 15, 'sslverify' => false, 'body' => $api_params));
162
-
163
- if (!is_wp_error($request)) {
164
- $request = json_decode(wp_remote_retrieve_body($request));
165
- if ($request && isset($request->sections)) {
166
- $request->sections = maybe_unserialize($request->sections);
167
- }
168
- return $request;
169
- } else {
170
- return false;
171
- }
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
1
  <?php
 
 
 
2
  /**
3
+ * Class: Plugin Updater.
4
+ *
5
+ * Allowes plugins to use their own update API.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ // Uncomment this line for testing.
16
+ // set_site_transient( 'update_plugins', null );
17
+
18
+ /**
19
+ * Class: Plugin Updater.
20
+ *
21
  * Allows plugins to use their own update API.
22
  *
23
  * @author Pippin Williamson
24
  * @version 1.2
25
+ * @package Wsal
26
  */
27
+ class EDD_SL_Plugin_Updater {
28
+
29
+ /**
30
+ * API URL.
31
+ *
32
+ * @var string
33
+ */
34
+ private $api_url = '';
35
+
36
+ /**
37
+ * API Data.
38
+ *
39
+ * @var array
40
+ */
41
+ private $api_data = array();
42
+
43
+ /**
44
+ * Plugin Name.
45
+ *
46
+ * @var string
47
+ */
48
+ private $name = '';
49
+
50
+ /**
51
+ * SLug.
52
+ *
53
+ * @var string
54
+ */
55
+ private $slug = '';
56
+
57
+ /**
58
+ * Check?
59
+ *
60
+ * @var bool
61
+ */
62
+ private $do_check = false;
63
+
64
+ /**
65
+ * Class constructor.
66
+ *
67
+ * @uses plugin_basename()
68
+ * @uses hook()
69
+ *
70
+ * @param string $_api_url The URL pointing to the custom API endpoint.
71
+ * @param string $_plugin_file Path to the plugin file.
72
+ * @param array $_api_data Optional data to send with API calls.
73
+ * @return void
74
+ */
75
+ public function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
76
+ $this->api_url = trailingslashit( $_api_url );
77
+ $this->api_data = urlencode_deep( $_api_data );
78
+ $this->name = plugin_basename( $_plugin_file );
79
+ $this->slug = basename( $_plugin_file, '.php' );
80
+ $this->version = $_api_data['version'];
81
+
82
+ // Set up hooks.
83
+ $this->hook();
84
+ }
85
+
86
+ /**
87
+ * Set up WordPress filters to hook into WP's update process.
88
+ *
89
+ * @uses add_filter()
90
+ * @return void
91
+ */
92
+ private function hook() {
93
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'pre_set_site_transient_update_plugins_filter' ) );
94
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
95
+ add_filter( 'http_request_args', array( $this, 'http_request_args' ), 10, 2 );
96
+ }
97
+
98
+ /**
99
+ * Check for Updates at the defined API endpoint and modify the update array.
100
+ *
101
+ * This function dives into the update API just when WordPress creates its update array,
102
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
103
+ * It is reassembled from parts of the native WordPress plugin update code.
104
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
105
+ *
106
+ * @uses api_request()
107
+ *
108
+ * @param array $_transient_data Update array build by WordPress.
109
+ * @return array Modified update array with custom plugin data.
110
+ */
111
+ public function pre_set_site_transient_update_plugins_filter( $_transient_data ) {
112
+ if ( empty( $_transient_data ) || ! $this->do_check ) {
113
+ // This ensures that the custom API request only runs on the second time that WP fires the update check.
114
+ $this->do_check = true;
115
+ return $_transient_data;
116
+ }
117
+
118
+ $to_send = array(
119
+ 'slug' => $this->slug,
120
+ );
121
+ $api_response = $this->api_request( 'plugin_latest_version', $to_send );
122
+
123
+ if ( false !== $api_response && is_object( $api_response ) && isset( $api_response->new_version ) ) {
124
+ if ( version_compare( $this->version, $api_response->new_version, '<' ) ) {
125
+ $_transient_data->response[ $this->name ] = $api_response;
126
+ }
127
+ }
128
+ return $_transient_data;
129
+ }
130
+
131
+ /**
132
+ * Updates information on the "View version x.x details" page with custom data.
133
+ *
134
+ * @uses api_request()
135
+ *
136
+ * @param mixed $_data - Data.
137
+ * @param string $_action - Action.
138
+ * @param object $_args - Arguments.
139
+ * @return object $_data
140
+ */
141
+ public function plugins_api_filter( $_data, $_action = '', $_args = null ) {
142
+ if ( ( 'plugin_information' != $_action ) || ! isset( $_args->slug ) || ($_args->slug != $this->slug) ) {
143
+ return $_data;
144
+ }
145
+ $to_send = array(
146
+ 'slug' => $this->slug,
147
+ );
148
+
149
+ $api_response = $this->api_request( 'plugin_information', $to_send );
150
+ if ( false !== $api_response ) {
151
+ $_data = $api_response;
152
+ }
153
+
154
+ return $_data;
155
+ }
156
+
157
+ /**
158
+ * Disable SSL verification in order to prevent download update failures
159
+ *
160
+ * @param array $args - Arguments.
161
+ * @param string $url - URL.
162
+ * @return array $args
163
+ */
164
+ public function http_request_args( $args, $url ) {
165
+ // If it is an https request and we are performing a package download, disable ssl verification.
166
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
167
+ $args['sslverify'] = false;
168
+ }
169
+ return $args;
170
+ }
171
+
172
+ /**
173
+ * Calls the API and, if successfull, returns the object delivered by the API.
174
+ *
175
+ * @uses get_bloginfo()
176
+ * @uses wp_remote_post()
177
+ * @uses is_wp_error()
178
+ *
179
+ * @param string $_action The requested action.
180
+ * @param array $_data Parameters for the API action.
181
+ * @return false||object
182
+ */
183
+ private function api_request( $_action, $_data ) {
184
+ global $wp_version;
185
+ $data = array_merge( $this->api_data, $_data );
186
+
187
+ if ( $data['slug'] != $this->slug ) {
188
+ return;
189
+ }
190
+
191
+ if ( empty( $data['license'] ) ) {
192
+ return;
193
+ }
194
+
195
+ $api_params = array(
196
+ 'edd_action' => 'get_version',
197
+ 'license' => $data['license'],
198
+ 'name' => $data['item_name'],
199
+ 'slug' => $this->slug,
200
+ 'author' => $data['author'],
201
+ 'url' => home_url(),
202
+ );
203
+ $request = wp_remote_post(
204
+ $this->api_url, array(
205
+ 'timeout' => 15,
206
+ 'sslverify' => false,
207
+ 'body' => $api_params,
208
+ )
209
+ );
210
+
211
+ if ( ! is_wp_error( $request ) ) {
212
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
213
+ if ( $request && isset( $request->sections ) ) {
214
+ $request->sections = maybe_unserialize( $request->sections );
215
+ }
216
+ return $request;
217
+ } else {
218
+ return false;
219
+ }
220
+ }
221
  }
classes/Helpers/DataHelper.php CHANGED
@@ -1,28 +1,41 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
5
  * Helper class used for encode/decode json data.
 
 
6
  */
7
- class WSAL_Helpers_DataHelper
8
- {
9
- /**
10
- * A wrapper for JSON encoding that fixes potential issues.
11
- * @param mixed $data The data to encode.
12
- * @return string JSON string.
13
- */
14
- public static function JsonEncode($data)
15
- {
16
- return @json_encode($data);
17
- }
18
-
19
- /**
20
- * A wrapper for JSON encoding that fixes potential issues.
21
- * @param string $data The JSON string to decode.
22
- * @return mixed Decoded data.
23
- */
24
- public static function JsonDecode($data)
25
- {
26
- return @json_decode($data);
27
- }
28
  }
1
  <?php
2
  /**
3
+ * Class: Data Helper.
4
+ *
5
+ * Helper class used for encode/decode json data..
6
  *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * Helper class used for encode/decode json data.
17
+ *
18
+ * @package Wsal
19
  */
20
+ class WSAL_Helpers_DataHelper {
21
+
22
+ /**
23
+ * A wrapper for JSON encoding that fixes potential issues.
24
+ *
25
+ * @param mixed $data The data to encode.
26
+ * @return string JSON string.
27
+ */
28
+ public static function JsonEncode( $data ) {
29
+ return @json_encode( $data );
30
+ }
31
+
32
+ /**
33
+ * A wrapper for JSON encoding that fixes potential issues.
34
+ *
35
+ * @param string $data - The JSON string to decode.
36
+ * @return mixed Decoded data.
37
+ */
38
+ public static function JsonDecode( $data ) {
39
+ return @json_decode( $data );
40
+ }
41
  }
classes/LicenseManager.php CHANGED
@@ -1,196 +1,268 @@
1
  <?php
 
 
 
 
 
 
 
2
 
3
- // since other plugins might use this class
4
- if(!class_exists('EDD_SL_Plugin_Updater')){
5
- require_once('EDD_SL_Plugin_Updater.php');
6
  }
 
 
 
 
 
 
7
  /**
8
- * @package Wsal
9
  *
10
  * License Manager used in all the Add-Ons licenses.
 
 
11
  */
12
- class WSAL_LicenseManager
13
- {
14
- /**
15
- * @var WpSecurityAuditLog
16
- */
17
- protected $plugin;
18
-
19
- protected $plugins = array();
20
-
21
- public function __construct(WpSecurityAuditLog $plugin)
22
- {
23
- $this->plugin = $plugin;
24
- add_action('plugins_loaded', array($this, 'LoadPlugins'));
25
- }
26
-
27
- protected function GetStoreUrl()
28
- {
29
- return 'https://www.wpsecurityauditlog.com/';
30
- }
31
-
32
- public function CountPlugins()
33
- {
34
- return count($this->plugins);
35
- }
36
-
37
- public function Plugins()
38
- {
39
- return $this->plugins;
40
- }
41
-
42
- public function LoadPlugins()
43
- {
44
- foreach (apply_filters('wsal_register', array()) as $pluginFile) {
45
- $this->AddPremiumPlugin($pluginFile);
46
- }
47
- }
48
-
49
- protected function GetPluginData($pluginFile, $license)
50
- {
51
- // A hack since get_plugin_data() is not available now
52
- $pluginData = get_file_data($pluginFile, array(
53
- 'Name' => 'Plugin Name',
54
- 'PluginURI' => 'Plugin URI',
55
- 'Version' => 'Version',
56
- 'Description' => 'Description',
57
- 'Author' => 'Author',
58
- 'TextDomain' => 'Text Domain',
59
- 'DomainPath' => 'Domain Path',
60
- ), 'plugin');
61
-
62
- $pluginUpdater = is_null($license)
63
- ? null
64
- : new EDD_SL_Plugin_Updater(
65
- $this->GetStoreUrl(),
66
- $pluginFile,
67
- array(
68
- 'license' => $license,
69
- 'item_name' => $pluginData['Name'],
70
- 'author' => $pluginData['Author'],
71
- 'version' => $pluginData['Version'],
72
- )
73
- );
74
-
75
- return array(
76
- 'PluginData' => $pluginData,
77
- 'EddUpdater' => $pluginUpdater,
78
- );
79
- }
80
-
81
- public function AddPremiumPlugin($pluginFile)
82
- {
83
- if (isset($pluginFile)) {
84
- $name = sanitize_key(basename($pluginFile));
85
- $license = $this->plugin->settings->GetLicenseKey($name);
86
- $this->plugins[$name] = $this->GetPluginData($pluginFile, $license);
87
- }
88
- }
89
-
90
- public function AddPlugin($pluginFile)
91
- {
92
- if (isset($pluginFile)) {
93
- $name = sanitize_key(basename($pluginFile));
94
- $this->plugins[$name] = $this->GetPluginData($pluginFile, null);
95
- }
96
- }
97
-
98
- protected function GetBlogIds()
99
- {
100
- global $wpdb;
101
- $sql = 'SELECT blog_id FROM ' . $wpdb->blogs;
102
- return $wpdb->get_col($sql);
103
- }
104
-
105
- public function ActivateLicense($name, $license)
106
- {
107
- $this->plugin->settings->SetLicenseKey($name, $license);
108
-
109
- $plugins = $this->Plugins();
110
- $api_params = array(
111
- 'edd_action'=> 'activate_license',
112
- 'license' => urlencode($license),
113
- 'item_name' => urlencode($plugins[$name]['PluginData']['Name']),
114
- 'url' => urlencode(home_url()),
115
- );
116
-
117
- $blog_ids = $this->plugin->IsMultisite() ? $this->GetBlogIds() : array(1);
118
-
119
- foreach ($blog_ids as $blog_id) {
120
- if ($this->plugin->IsMultisite()) {
121
- $api_params['url'] = urlencode(get_home_url($blog_id));
122
- }
123
-
124
- $response = wp_remote_get(
125
- esc_url_raw(add_query_arg($api_params, $this->GetStoreUrl())),
126
- array('timeout' => 15, 'sslverify' => false)
127
- );
128
-
129
- if (is_wp_error($response)) {
130
- $this->plugin->settings->SetLicenseErrors($name, 'Invalid Licensing Server Response: ' . $response->get_error_message());
131
- $this->DeactivateLicense($name, $license);
132
- return false;
133
- }
134
-
135
- $license_data = json_decode(wp_remote_retrieve_body($response));
136
-
137
- if (is_object($license_data)) {
138
- $this->plugin->settings->SetLicenseStatus($name, $license_data->license);
139
- if ($license_data->license !== 'valid') {
140
- $error = 'License Not Valid';
141
- if (isset($license_data->error)) {
142
- $error .= ': ' . ucfirst(str_replace('_', ' ', $license_data->error));
143
- }
144
- $this->plugin->settings->SetLicenseErrors($name, $error);
145
- $this->DeactivateLicense($name, $license);
146
- return false;
147
- }
148
- } else {
149
- $this->plugin->settings->SetLicenseErrors($name, 'Unexpected Licensing Server Response');
150
- $this->DeactivateLicense($name, $license);
151
- return false;
152
- }
153
- }
154
-
155
- return true;
156
- }
157
-
158
- public function IsLicenseValid($name)
159
- {
160
- return trim(strtolower($this->plugin->settings->GetLicenseStatus($name))) === 'valid';
161
- }
162
-
163
- public function DeactivateLicense($name, $license = null)
164
- {
165
- $this->plugin->settings->SetLicenseStatus($name, '');
166
- // deactivate it on the server (if license was given)
167
- if (!is_null($license)) {
168
- $plugins = $this->Plugins();
169
- $api_params = array(
170
- 'edd_action'=> 'deactivate_license',
171
- 'license' => urlencode($license),
172
- 'item_name' => urlencode($plugins[$name]['PluginData']['Name']),
173
- 'url' => urlencode(home_url()),
174
- );
175
-
176
- $blog_ids = $this->plugin->IsMultisite() ? $this->GetBlogIds() : array(1);
177
-
178
- foreach ($blog_ids as $blog_id) {
179
- if ($this->plugin->IsMultisite()) {
180
- $api_params['url'] = urlencode(get_home_url($blog_id));
181
- }
182
-
183
- $response = wp_remote_get(
184
- esc_url_raw(add_query_arg($api_params, $this->GetStoreUrl())),
185
- array('timeout' => 15, 'sslverify' => false)
186
- );
187
-
188
- if (is_wp_error($response)) {
189
- return false;
190
- }
191
-
192
- wp_remote_retrieve_body($response);
193
- }
194
- }
195
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  }
1
  <?php
2
+ /**
3
+ * Class: License Manager.
4
+ *
5
+ * License manager used in all the Add-Ons licenses.
6
+ *
7
+ * @package Wsal
8
+ */
9
 
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
  }
14
+
15
+ // Since other plugins might use this class.
16
+ if ( ! class_exists( 'EDD_SL_Plugin_Updater' ) ) {
17
+ require_once( 'EDD_SL_Plugin_Updater.php' );
18
+ }
19
+
20
  /**
21
+ * Class: License Manager.
22
  *
23
  * License Manager used in all the Add-Ons licenses.
24
+ *
25
+ * @package Wsal
26
  */
27
+ class WSAL_LicenseManager {
28
+
29
+ /**
30
+ * Instance of WpSecurityAuditLog.
31
+ *
32
+ * @var WpSecurityAuditLog
33
+ */
34
+ protected $plugin;
35
+
36
+ /**
37
+ * Array of Add-Ons.
38
+ *
39
+ * @var array
40
+ */
41
+ protected $plugins = array();
42
+
43
+ /**
44
+ * Method: Constructor.
45
+ *
46
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
47
+ */
48
+ public function __construct( WpSecurityAuditLog $plugin ) {
49
+ $this->plugin = $plugin;
50
+ add_action( 'plugins_loaded', array( $this, 'LoadPlugins' ) );
51
+ }
52
+
53
+ /**
54
+ * Method: Get Store URL.
55
+ */
56
+ protected function GetStoreUrl() {
57
+ return 'https://www.wpsecurityauditlog.com/';
58
+ }
59
+
60
+ /**
61
+ * Method: Count Add-Ons.
62
+ */
63
+ public function CountPlugins() {
64
+ return count( $this->plugins );
65
+ }
66
+
67
+ /**
68
+ * Method: Get Add-Ons.
69
+ */
70
+ public function Plugins() {
71
+ return $this->plugins;
72
+ }
73
+
74
+ /**
75
+ * Method: Load Add-Ons.
76
+ */
77
+ public function LoadPlugins() {
78
+ foreach ( apply_filters( 'wsal_register', array() ) as $plugin_file ) {
79
+ $this->AddPremiumPlugin( $plugin_file );
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Method: Get Plugin Data.
85
+ *
86
+ * @param string $plugin_file - Plugin file.
87
+ * @param string $license - Plugin License.
88
+ */
89
+ protected function GetPluginData( $plugin_file, $license ) {
90
+ // A hack since get_plugin_data() is not available now.
91
+ $plugin_data = get_file_data(
92
+ $plugin_file, array(
93
+ 'Name' => 'Plugin Name',
94
+ 'PluginURI' => 'Plugin URI',
95
+ 'Version' => 'Version',
96
+ 'Description' => 'Description',
97
+ 'Author' => 'Author',
98
+ 'TextDomain' => 'Text Domain',
99
+ 'DomainPath' => 'Domain Path',
100
+ ), 'plugin'
101
+ );
102
+
103
+ $plugin_updater = is_null( $license )
104
+ ? null
105
+ : new EDD_SL_Plugin_Updater(
106
+ $this->GetStoreUrl(),
107
+ $plugin_file,
108
+ array(
109
+ 'license' => $license,
110
+ 'item_name' => $plugin_data['Name'],
111
+ 'author' => $plugin_data['Author'],
112
+ 'version' => $plugin_data['Version'],
113
+ )
114
+ );
115
+
116
+ return array(
117
+ 'PluginData' => $plugin_data,
118
+ 'EddUpdater' => $plugin_updater,
119
+ );
120
+ }
121
+
122
+ /**
123
+ * Method: Add Premium Plugin.
124
+ *
125
+ * @param string $plugin_file - Plugin File.
126
+ */
127
+ public function AddPremiumPlugin( $plugin_file ) {
128
+ if ( isset( $plugin_file ) ) {
129
+ $name = sanitize_key( basename( $plugin_file ) );
130
+ $license = $this->plugin->settings->GetLicenseKey( $name );
131
+ $this->plugins[ $name ] = $this->GetPluginData( $plugin_file, $license );
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Method: Add Plugin.
137
+ *
138
+ * @param string $plugin_file - Plugin File.
139
+ */
140
+ public function AddPlugin( $plugin_file ) {
141
+ if ( isset( $plugin_file ) ) {
142
+ $name = sanitize_key( basename( $plugin_file ) );
143
+ $this->plugins[ $name ] = $this->GetPluginData( $plugin_file, null );
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Method: Get Blog IDs.
149
+ */
150
+ protected function GetBlogIds() {
151
+ global $wpdb;
152
+ $sql = 'SELECT blog_id FROM ' . $wpdb->blogs;
153
+ return $wpdb->get_col( $sql );
154
+ }
155
+
156
+ /**
157
+ * Method: Activate Premium Plugin license.
158
+ *
159
+ * @param string $name - Plugin name.
160
+ * @param string $license - Plugin license.
161
+ */
162
+ public function ActivateLicense( $name, $license ) {
163
+ $this->plugin->settings->SetLicenseKey( $name, $license );
164
+
165
+ $plugins = $this->Plugins();
166
+ $api_params = array(
167
+ 'edd_action' => 'activate_license',
168
+ 'license' => urlencode( $license ),
169
+ 'item_name' => urlencode( $plugins[ $name ]['PluginData']['Name'] ),
170
+ 'url' => urlencode( home_url() ),
171
+ );
172
+
173
+ $blog_ids = $this->plugin->IsMultisite() ? $this->GetBlogIds() : array( 1 );
174
+
175
+ foreach ( $blog_ids as $blog_id ) {
176
+ if ( $this->plugin->IsMultisite() ) {
177
+ $api_params['url'] = urlencode( get_home_url( $blog_id ) );
178
+ }
179
+
180
+ $response = wp_remote_get(
181
+ esc_url_raw( add_query_arg( $api_params, $this->GetStoreUrl() ) ),
182
+ array(
183
+ 'timeout' => 15,
184
+ 'sslverify' => false,
185
+ )
186
+ );
187
+
188
+ if ( is_wp_error( $response ) ) {
189
+ $this->plugin->settings->SetLicenseErrors( $name, 'Invalid Licensing Server Response: ' . $response->get_error_message() );
190
+ $this->DeactivateLicense( $name, $license );
191
+ return false;
192
+ }
193
+
194
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
195
+
196
+ if ( is_object( $license_data ) ) {
197
+ $this->plugin->settings->SetLicenseStatus( $name, $license_data->license );
198
+ if ( 'valid' !== $license_data->license ) {
199
+ $error = 'License Not Valid';
200
+ if ( isset( $license_data->error ) ) {
201
+ $error .= ': ' . ucfirst( str_replace( '_', ' ', $license_data->error ) );
202
+ }
203
+ $this->plugin->settings->SetLicenseErrors( $name, $error );
204
+ $this->DeactivateLicense( $name, $license );
205
+ return false;
206
+ }
207
+ } else {
208
+ $this->plugin->settings->SetLicenseErrors( $name, 'Unexpected Licensing Server Response' );
209
+ $this->DeactivateLicense( $name, $license );
210
+ return false;
211
+ }
212
+ }
213
+
214
+ return true;
215
+ }
216
+
217
+ /**
218
+ * Method: Check Plugin License.
219
+ *
220
+ * @param string $name - Plugin name.
221
+ */
222
+ public function IsLicenseValid( $name ) {
223
+ return trim( strtolower( $this->plugin->settings->GetLicenseStatus( $name ) ) ) === 'valid';
224
+ }
225
+
226
+ /**
227
+ * Method: Deactivate Premium Plugin license.
228
+ *
229
+ * @param string $name - Plugin name.
230
+ * @param string $license - Plugin license.
231
+ */
232
+ public function DeactivateLicense( $name, $license = null ) {
233
+ $this->plugin->settings->SetLicenseStatus( $name, '' );
234
+
235
+ // Deactivate it on the server (if license was given).
236
+ if ( ! is_null( $license ) ) {
237
+ $plugins = $this->Plugins();
238
+ $api_params = array(
239
+ 'edd_action' => 'deactivate_license',
240
+ 'license' => urlencode( $license ),
241
+ 'item_name' => urlencode( $plugins[ $name ]['PluginData']['Name'] ),
242
+ 'url' => urlencode( home_url() ),
243
+ );
244
+
245
+ $blog_ids = $this->plugin->IsMultisite() ? $this->GetBlogIds() : array( 1 );
246
+
247
+ foreach ( $blog_ids as $blog_id ) {
248
+ if ( $this->plugin->IsMultisite() ) {
249
+ $api_params['url'] = urlencode( get_home_url( $blog_id ) );
250
+ }
251
+
252
+ $response = wp_remote_get(
253
+ esc_url_raw( add_query_arg( $api_params, $this->GetStoreUrl() ) ),
254
+ array(
255
+ 'timeout' => 15,
256
+ 'sslverify' => false,
257
+ )
258
+ );
259
+
260
+ if ( is_wp_error( $response ) ) {
261
+ return false;
262
+ }
263
+
264
+ wp_remote_retrieve_body( $response );
265
+ }
266
+ }
267
+ }
268
  }
classes/Loggers/Database.php CHANGED
@@ -1,207 +1,217 @@
1
- <?php
2
- /**
3
- * @package Wsal
4
- * Loggers Class.
5
- *
6
- * This class store the logs in the Database and adds the promo
7
- * alerts, there is also the function to clean up alerts.
8
- */
9
- class WSAL_Loggers_Database extends WSAL_AbstractLogger
10
- {
11
- public function __construct(WpSecurityAuditLog $plugin)
12
- {
13
- parent::__construct($plugin);
14
- $plugin->AddCleanupHook(array($this, 'CleanUp'));
15
- }
16
-
17
- /**
18
- * Log alert.
19
- * @param integer $type alert code
20
- * @param array $data Metadata
21
- * @param integer $date (Optional) created_on
22
- * @param integer $siteid (Optional) site_id
23
- * @param bool $migrated (Optional) is_migrated
24
- */
25
- public function Log($type, $data = array(), $date = null, $siteid = null, $migrated = false)
26
- {
27
- // is this a php alert, and if so, are we logging such alerts?
28
- if ($type < 0010 && !$this->plugin->settings->IsPhpErrorLoggingEnabled()) {
29
- return;
30
- }
31
-
32
- // create new occurrence
33
- $occ = new WSAL_Models_Occurrence();
34
- $occ->is_migrated = $migrated;
35
- $occ->created_on = $date;
36
- $occ->alert_id = $type;
37
- $occ->site_id = !is_null($siteid) ? $siteid
38
- : (function_exists('get_current_blog_id') ? get_current_blog_id() : 0);
39
- $occ->Save();
40
-
41
- // set up meta data
42
- $occ->SetMeta($data);
43
-
44
- // Inject for promoting the paid add-ons
45
- if ($type != 9999) {
46
- $this->AlertInject($occ);
47
- }
48
- }
49
-
50
- /**
51
- * Clean Up alerts by date OR by max number.
52
- */
53
- public function CleanUp()
54
- {
55
- $now = current_time('timestamp');
56
- $max_sdate = $this->plugin->settings->GetPruningDate();
57
- $max_count = $this->plugin->settings->GetPruningLimit();
58
- $is_date_e = $this->plugin->settings->IsPruningDateEnabled();
59
- $is_limt_e = $this->plugin->settings->IsPruningLimitEnabled();
60
-
61
- if (!$is_date_e && !$is_limt_e) {
62
- return;
63
- } // pruning disabled
64
- $occ = new WSAL_Models_Occurrence();
65
- $cnt_items = $occ->Count();
66
-
67
- // Check if there is something to delete
68
- if ($is_limt_e && ($cnt_items < $max_count)) {
69
- return;
70
- }
71
-
72
- $max_stamp = $now - (strtotime($max_sdate) - $now);
73
- $max_items = (int)max(($cnt_items - $max_count) + 1, 0);
74
-
75
- $query = new WSAL_Models_OccurrenceQuery();
76
- $query->addOrderBy("created_on", false);
77
- // TO DO Fixing data
78
- if ($is_date_e) {
79
- $query->addCondition('created_on <= %s', intval($max_stamp));
80
- }
81
- if ($is_limt_e) {
82
- $query->setLimit($max_items);
83
- }
84
-
85
- if (($max_items-1) == 0) {
86
- return; // nothing to delete
87
- }
88
-
89
- $result = $query->getAdapter()->GetSqlDelete($query);
90
- $deletedCount = $query->getAdapter()->Delete($query);
91
-
92
- if ($deletedCount == 0) {
93
- return; // nothing to delete
94
- }
95
- // keep track of what we're doing
96
- $this->plugin->alerts->Trigger(0003, array(
97
- 'Message' => 'Running system cleanup.',
98
- 'Query SQL' => $result['sql'],
99
- 'Query Args' => $result['args'],
100
- ), true);
101
-
102
- // notify system
103
- do_action('wsal_prune', $deletedCount, vsprintf($result['sql'], $result['args']));
104
- }
105
-
106
- /**
107
- * Inject Promo alert every $count alerts if no Add-ons are activated.
108
- * @param WSAL_Models_Occurrence $occurrence occurrence
109
- */
110
- private function AlertInject($occurrence)
111
- {
112
- $count = $this->CheckPromoToShow();
113
- if ($count && $occurrence->getId() != 0) {
114
- if (($occurrence->getId() % $count) == 0) {
115
- $promoToSend = $this->GetPromoAlert();
116
- if (!empty($promoToSend)) {
117
- $link = '<a href="'.$promoToSend['link'].'" target="_blank">Upgrade Now</a>';
118
- $this->Log(9999, array(
119
- 'ClientIP' => '127.0.0.1',
120
- 'Username' => 'Plugin',
121
- 'PromoMessage' => sprintf($promoToSend['message'], $link),
122
- 'PromoName' => $promoToSend['name']
123
- ));
124
- }
125
- }
126
- }
127
- }
128
-
129
- /**
130
- * Get the promo id, to send each time a different promo,
131
- * keeping the last id saved in the DB.
132
- * @return integer $promoToSend the array index
133
- */
134
- private function GetPromoAlert()
135
- {
136
- $lastPromoSentId = $this->plugin->GetGlobalOption('promo-send-id');
137
- $lastPromoSentId = empty($lastPromoSentId) ? 0 : $lastPromoSentId;
138
- $promoToSend = null;
139
- $aPromoAlerts = $this->GetActivePromoText();
140
- if (!empty($aPromoAlerts)) {
141
- $promoToSend = isset($aPromoAlerts[$lastPromoSentId]) ? $aPromoAlerts[$lastPromoSentId] : $aPromoAlerts[0];
142
-
143
- if ($lastPromoSentId < count($aPromoAlerts)-1) {
144
- $lastPromoSentId++;
145
- } else {
146
- $lastPromoSentId = 0;
147
- }
148
- $this->plugin->SetGlobalOption('promo-send-id', $lastPromoSentId);
149
- }
150
- return $promoToSend;
151
- }
152
-
153
- /**
154
- * Array of promo.
155
- * @return array $aPromoAlerts the array of promo
156
- */
157
- private function GetActivePromoText()
158
- {
159
- $aPromoAlerts = array();
160
- $aPromoAlerts[] = array(
161
- 'name' => 'Upgrade to Premium',
162
- 'message' => 'Add email alerts, see who is logged in, generate reports, add search and other functionality by upgrading to Premium for just $89. <strong>%s</strong>',
163
- 'link' => 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=auditviewer&utm_medium=promoalert&utm_campaign=plugin'
164
- );
165
- $aPromoAlerts[] = array(
166
- 'name' => 'Get 70% Discount When You Upgrade to Premium',
167
- 'message' => 'Benefit from a discount of 70&percnt; upgrade to premium for just $89 and add <strong>Email Alerts</strong>, <strong>User Logins Management</strong>, <strong>Search</strong> and <strong>Reporting</strong> functionality to the plugin. <strong>%s</strong>',
168
- 'link' => 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=auditviewer&utm_medium=promoalert&utm_campaign=plugin'
169
- );
170
- $aPromoAlerts[] = array(
171
- 'name' => 'Add Email Alerts, Search, Generate Reports and See Who is Logged In',
172
- 'message' => 'Upgrade to premium and extend the plugin’s features with email alerts, report generator, free-text based search and user logins and sessions management. Benefit from a 70&percnt; discount. Prices starts at just $89 <strong>%s</strong>',
173
- 'link' => 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=auditviewer&utm_medium=promoalert&utm_campaign=plugin'
174
- );
175
- return $aPromoAlerts;
176
- }
177
-
178
- /**
179
- * Check condition to show promo.
180
- * @return integer|null counter alert
181
- */
182
- private function CheckPromoToShow()
183
- {
184
- $promoToShow = null;
185
- // Check: Email Add-On, Search Add-On, Reports Add-On, External DB Add-On, Manage Users Sessions Add-on
186
- if (!class_exists('WSAL_NP_Plugin')) {
187
- $promoToShow[] = true;
188
- }
189
- if (!class_exists('WSAL_SearchExtension')) {
190
- $promoToShow[] = true;
191
- }
192
- if (!class_exists('WSAL_Rep_Plugin')) {
193
- $promoToShow[] = true;
194
- }
195
- if (!class_exists('WSAL_Ext_Plugin')) {
196
- $promoToShow[] = true;
197
- }
198
- if (!class_exists('WSAL_User_Management_Plugin')) {
199
- $promoToShow[] = true;
200
- }
201
-
202
- if (empty($promoToShow)) {
203
- return null;
204
- }
205
- return (count($promoToShow) == 5) ? 80 : null;
206
- }
207
- }
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Class: Logger
4
+ *
5
+ * Logger class for wsal.
6
+ *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
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
+
26
+ /**
27
+ * Method: Constructor.
28
+ *
29
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
30
+ * @since 1.0.0
31
+ */
32
+ public function __construct( WpSecurityAuditLog $plugin ) {
33
+ parent::__construct( $plugin );
34
+ $plugin->AddCleanupHook( array( $this, 'CleanUp' ) );
35
+ }
36
+
37
+ /**
38
+ * Log alert.
39
+ *
40
+ * @param integer $type - Alert code.
41
+ * @param array $data - Metadata.
42
+ * @param integer $date (Optional) - created_on.
43
+ * @param integer $siteid (Optional) - site_id.
44
+ * @param bool $migrated (Optional) - is_migrated.
45
+ */
46
+ public function Log( $type, $data = array(), $date = null, $siteid = null, $migrated = false ) {
47
+ // Is this a php alert, and if so, are we logging such alerts?
48
+ if ( $type < 0010 && ! $this->plugin->settings->IsPhpErrorLoggingEnabled() ) {
49
+ return;
50
+ }
51
+
52
+ // Create new occurrence.
53
+ $occ = new WSAL_Models_Occurrence();
54
+ $occ->is_migrated = $migrated;
55
+ $occ->created_on = $date;
56
+ $occ->alert_id = $type;
57
+ $occ->site_id = ! is_null( $siteid ) ? $siteid
58
+ : (function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 0);
59
+ $occ->Save();
60
+
61
+ // Set up meta data.
62
+ $occ->SetMeta( $data );
63
+
64
+ // Inject for promoting the paid add-ons.
65
+ if ( 9999 != $type ) {
66
+ $this->AlertInject( $occ );
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Clean Up alerts by date OR by max number.
72
+ */
73
+ public function CleanUp() {
74
+ $now = current_time( 'timestamp' );
75
+ $max_sdate = $this->plugin->settings->GetPruningDate();
76
+ $max_count = $this->plugin->settings->GetPruningLimit();
77
+ $is_date_e = $this->plugin->settings->IsPruningDateEnabled();
78
+ $is_limt_e = $this->plugin->settings->IsPruningLimitEnabled();
79
+
80
+ if ( ! $is_date_e && ! $is_limt_e ) {
81
+ return;
82
+ } // Pruning disabled.
83
+ $occ = new WSAL_Models_Occurrence();
84
+ $cnt_items = $occ->Count();
85
+
86
+ // Check if there is something to delete.
87
+ if ( $is_limt_e && ($cnt_items < $max_count) ) {
88
+ return;
89
+ }
90
+
91
+ $max_stamp = $now - (strtotime( $max_sdate ) - $now);
92
+ $max_items = (int) max( ($cnt_items - $max_count) + 1, 0 );
93
+
94
+ $query = new WSAL_Models_OccurrenceQuery();
95
+ $query->addOrderBy( 'created_on', false );
96
+ // TO DO: Fixing data.
97
+ if ( $is_date_e ) {
98
+ $query->addCondition( 'created_on <= %s', intval( $max_stamp ) );
99
+ }
100
+ if ( $is_limt_e ) {
101
+ $query->setLimit( $max_items );
102
+ }
103
+
104
+ if ( ($max_items - 1) == 0 ) {
105
+ return; // Nothing to delete.
106
+ }
107
+
108
+ $result = $query->getAdapter()->GetSqlDelete( $query );
109
+ $deleted_count = $query->getAdapter()->Delete( $query );
110
+
111
+ if ( 0 == $deleted_count ) {
112
+ return; // Nothing to delete.
113
+ }
114
+ // Keep track of what we're doing.
115
+ $this->plugin->alerts->Trigger(
116
+ 0003, array(
117
+ 'Message' => 'Running system cleanup.',
118
+ 'Query SQL' => $result['sql'],
119
+ 'Query Args' => $result['args'],
120
+ ), true
121
+ );
122
+
123
+ // Notify system.
124
+ do_action( 'wsal_prune', $deleted_count, vsprintf( $result['sql'], $result['args'] ) );
125
+ }
126
+
127
+ /**
128
+ * Inject Promo alert every $count alerts if no Add-ons are activated.
129
+ *
130
+ * @param object $occurrence - Occurrence, instance of WSAL_Models_Occurrence.
131
+ */
132
+ private function AlertInject( $occurrence ) {
133
+ $count = $this->CheckPromoToShow();
134
+ if ( $count && $occurrence->getId() != 0 ) {
135
+ if ( ($occurrence->getId() % $count) == 0 ) {
136
+ $promo_to_send = $this->GetPromoAlert();
137
+ if ( ! empty( $promo_to_send ) ) {
138
+ $upgrade_link = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
139
+ $more_info_link = add_query_arg(
140
+ array(
141
+ 'utm_source' => 'alert',
142
+ 'utm_medium' => 'page',
143
+ 'utm_content' => 'alert+more+info',
144
+ 'utm_campaign' => 'upgrade+premium',
145
+ ),
146
+ 'https://www.wpsecurityauditlog.com/premium-features/'
147
+ );
148
+ $upgrade = '<a href="' . $upgrade_link . '">' . __( 'Upgrade to Premium', 'wp-security-audit-log' ) . '</a>';
149
+ $more_info = '<a href="' . $more_info_link . '" target="_blank">' . __( 'More Information', 'wp-security-audit-log' ) . '</a>';
150
+ $this->Log(
151
+ 9999, array(
152
+ 'ClientIP' => '127.0.0.1',
153
+ 'Username' => 'Plugin',
154
+ 'PromoMessage' => sprintf( $promo_to_send['message'], $upgrade, $more_info ),
155
+ 'PromoName' => $promo_to_send['name'],
156
+ )
157
+ );
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Get the promo id, to send each time a different promo,
165
+ * keeping the last id saved in the DB.
166
+ *
167
+ * @return integer $promoToSend - The array index.
168
+ */
169
+ private function GetPromoAlert() {
170
+ $last_promo_sent_id = $this->plugin->GetGlobalOption( 'promo-send-id' );
171
+ $last_promo_sent_id = empty( $last_promo_sent_id ) ? 0 : $last_promo_sent_id;
172
+ $promo_to_send = null;
173
+ $promo_alerts = $this->GetActivePromoText();
174
+ if ( ! empty( $promo_alerts ) ) {
175
+ $promo_to_send = isset( $promo_alerts[ $last_promo_sent_id ] ) ? $promo_alerts[ $last_promo_sent_id ] : $promo_alerts[0];
176
+
177
+ if ( $last_promo_sent_id < count( $promo_alerts ) - 1 ) {
178
+ $last_promo_sent_id++;
179
+ } else {
180
+ $last_promo_sent_id = 0;
181
+ }
182
+ $this->plugin->SetGlobalOption( 'promo-send-id', $last_promo_sent_id );
183
+ }
184
+ return $promo_to_send;
185
+ }
186
+
187
+ /**
188
+ * Array of promo.
189
+ *
190
+ * @return array $promo_alerts - The array of promo.
191
+ */
192
+ private function GetActivePromoText() {
193
+ $promo_alerts = array();
194
+ $promo_alerts[] = array(
195
+ 'name' => 'Upgrade to Premium',
196
+ '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>',
197
+ );
198
+ $promo_alerts[] = array(
199
+ 'name' => 'See Who is Logged In, receive Email Alerts, generate User Productivity Reports and more!',
200
+ '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>',
201
+ );
202
+ return $promo_alerts;
203
+ }
204
+
205
+ /**
206
+ * Check condition to show promo.
207
+ *
208
+ * @return integer|null - Counter alert.
209
+ */
210
+ private function CheckPromoToShow() {
211
+ // If the package is free, show the promo.
212
+ if ( wsal_freemius()->is_not_paying() ) {
213
+ return 80;
214
+ }
215
+ return null;
216
+ }
217
+ }
classes/Models/ActiveRecord.php CHANGED
@@ -1,344 +1,389 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
 
4
  *
 
 
 
 
 
 
 
 
 
5
  * Abstract ActiveRecord model is the generic model for any kind
6
  * of adapter.
 
 
7
  */
8
- abstract class WSAL_Models_ActiveRecord
9
- {
10
- const STATE_UNKNOWN = 'unknown';
11
- const STATE_CREATED = 'created';
12
- const STATE_UPDATED = 'updated';
13
- const STATE_DELETED = 'deleted';
14
- const STATE_LOADED = 'loaded';
15
-
16
- /**
17
- * @var $connector Data connector;
18
- */
19
- protected $connector;
20
-
21
- protected $id = false;
22
-
23
- protected $adapterName = null;
24
-
25
- protected $useDefaultAdapter = false;
26
-
27
- protected $_state = self::STATE_UNKNOWN;
28
-
29
- protected static $_cache = array();
30
-
31
- /**
32
- * @return array Returns this records' fields.
33
- */
34
- public function GetFields()
35
- {
36
- if (!isset($this->_column_cache)) {
37
- $this->_column_cache = array();
38
- foreach (array_keys(get_object_vars($this)) as $col) {
39
- if (trim($col) && $col[0] != '_') {
40
- $this->_column_cache[] = $col;
41
- }
42
- }
43
- }
44
- return $this->_column_cache;
45
- }
46
-
47
- /**
48
- * Sets the id.
49
- * @param integer $id
50
- */
51
- public function setId($id)
52
- {
53
- $this->id = $id;
54
- }
55
-
56
- /**
57
- * Gets the id.
58
- * @return integer $id.
59
- */
60
- public function getId()
61
- {
62
- return $this->id;
63
- }
64
-
65
- /**
66
- * @throws Exception requires adapterName
67
- */
68
- public function __construct($data = null)
69
- {
70
- if (!$this->adapterName) {
71
- throw new Exception('Class "' . __CLASS__ . '" requires "adapterName" to be set.');
72
- }
73
- if (!is_null($data)) {
74
- $this->LoadData($data);
75
- $this->_state = self::STATE_LOADED;
76
- }
77
- }
78
-
79
- /**
80
- * Gets the connector.
81
- * @return WSAL_Connector_ConnectorInterface
82
- */
83
- protected function getConnector()
84
- {
85
- if (!empty($this->connector)) {
86
- return $this->connector;
87
- }
88
- if ($this->useDefaultAdapter) {
89
- $this->connector = WSAL_Connector_ConnectorFactory::GetDefaultConnector();
90
- } else {
91
- $this->connector = WSAL_Connector_ConnectorFactory::GetConnector();
92
- }
93
- return $this->connector;
94
- }
95
-
96
- /**
97
- * Gets an adapter for the specified model
98
- * based on the adapter name.
99
- * @see WSAL_Connector_ConnectorInterface::getAdapter()
100
- */
101
- public function getAdapter()
102
- {
103
- return $this->getConnector()->getAdapter($this->adapterName);
104
- }
105
-
106
- /**
107
- * Load record from DB.
108
- * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
109
- * @param string $cond (Optional) Load condition.
110
- * @param array $args (Optional) Load condition arguments.
111
- */
112
- public function Load($cond = '%d', $args = array(1))
113
- {
114
- $this->_state = self::STATE_UNKNOWN;
115
-
116
- $data = $this->getAdapter()->Load($cond, $args);
117
- if (!is_null($data)) {
118
- $this->LoadData($data);
119
- $this->_state = self::STATE_LOADED;
120
- }
121
- }
122
-
123
- /**
124
- * Load object data from variable.
125
- * @param array|object $data Data array or object.
126
- */
127
- public function LoadData($data)
128
- {
129
- $copy = get_class($this);
130
- $copy = new $copy;
131
- foreach ((array)$data as $key => $val) {
132
- if (isset($copy->$key)) {
133
- switch (true) {
134
- case $this->is_ip_address($val):
135
- $this->$key = (string)$val;
136
- break;
137
- case is_array($copy->$key):
138
- case is_object($copy->$key):
139
- $jsonDecodedVal = WSAL_Helpers_DataHelper::JsonDecode($val);
140
- $this->$key = ($jsonDecodedVal == null) ? $val : $jsonDecodedVal;
141
- break;
142
- case is_int($copy->$key):
143
- $this->$key = (int)$val;
144
- break;
145
- case is_float($copy->$key):
146
- $this->$key = (float)$val;
147
- break;
148
- case is_bool($copy->$key):
149
- $this->$key = (bool)$val;
150
- break;
151
- case is_string($copy->$key):
152
- $this->$key = (string)$val;
153
- break;
154
- default:
155
- throw new Exception('Unsupported type "'.gettype($copy->$key).'"');
156
- }
157
- }
158
- }
159
- return $this;
160
- }
161
-
162
- /**
163
- * Save this active record
164
- * @see WSAL_Adapters_MySQL_ActiveRecord::Save()
165
- * @return integer|boolean Either the number of modified/inserted rows or false on failure.
166
- */
167
- public function Save()
168
- {
169
- $this->_state = self::STATE_UNKNOWN;
170
-
171
- // use today's date if not set up
172
- if (is_null($this->created_on)) {
173
- $this->created_on = $this->GetMicrotime();
174
- }
175
- $updateId = $this->getId();
176
- $result = $this->getAdapter()->Save($this);
177
-
178
- if ($result !== false) {
179
- $this->_state = (!empty($updateId))?self::STATE_UPDATED:self::STATE_CREATED;
180
- }
181
- return $result;
182
- }
183
-
184
- /**
185
- * Deletes this active record.
186
- * @see WSAL_Adapters_MySQL_ActiveRecord::Delete()
187
- * @return int|boolean Either the amount of deleted rows or False on error.
188
- */
189
- public function Delete()
190
- {
191
- $this->_state = self::STATE_UNKNOWN;
192
- $result = $this->getAdapter()->Delete($this);
193
- if ($result !== false) {
194
- $this->_state = self::STATE_DELETED;
195
- }
196
-
197
- return $result;
198
- }
199
-
200
- /**
201
- * Count records that matching a condition.
202
- * @see WSAL_Adapters_MySQL_ActiveRecord::Count()
203
- * @return int count
204
- */
205
- public function Count($cond = '%d', $args = array(1))
206
- {
207
- $result = $this->getAdapter()->Count($cond, $args);
208
- return $result;
209
- }
210
-
211
- /**
212
- * Check state loaded.
213
- * @return bool
214
- */
215
- public function IsLoaded()
216
- {
217
- return $this->_state == self::STATE_LOADED;
218
- }
219
-
220
- /**
221
- * Check state saved.
222
- * @return bool
223
- */
224
- public function IsSaved()
225
- {
226
- return $this->_state == self::STATE_CREATED
227
- || $this->_state == self::STATE_UPDATED;
228
- }
229
-
230
- /**
231
- * Check state created.
232
- * @return bool
233
- */
234
- public function IsCreated()
235
- {
236
- return $this->_state == self::STATE_CREATED;
237
- }
238
-
239
- /**
240
- * Check state updated.
241
- * @return bool
242
- */
243
- public function IsUpdated()
244
- {
245
- return $this->_state == self::STATE_UPDATED;
246
- }
247
-
248
- /**
249
- * Check if the Record structure is created.
250
- * @see WSAL_Adapters_MySQL_ActiveRecord::IsInstalled()
251
- * @return bool
252
- */
253
- public function IsInstalled()
254
- {
255
- return $this->getAdapter()->IsInstalled();
256
- }
257
-
258
- /**
259
- * Install the Record structure.
260
- * @see WSAL_Adapters_MySQL_ActiveRecord::Install()
261
- */
262
- public function Install()
263
- {
264
- return $this->getAdapter()->Install();
265
- }
266
-
267
- /**
268
- * Check state deleted.
269
- * @return bool
270
- */
271
- public function IsDeleted()
272
- {
273
- return $this->_state == self::STATE_DELETED;
274
- }
275
-
276
- /**
277
- * Load ActiveRecord from DB or cache.
278
- * @param string $target ActiveRecord class name.
279
- * @param string $query Load condition.
280
- * @param array $args Arguments used in condition.
281
- * @return WSAL_Models_ActiveRecord
282
- */
283
- protected static function CacheLoad($target, $query, $args)
284
- {
285
- $index = $target . '::' . vsprintf($query, $args);
286
- if (!isset(self::$_cache[$index])) {
287
- self::$_cache[$index] = new $target();
288
- self::$_cache[$index]->Load($query, $args);
289
- }
290
- return self::$_cache[$index];
291
- }
292
-
293
- /**
294
- * Remove ActiveRecord cache.
295
- * @param string $target ActiveRecord class name.
296
- * @param string $query Load condition.
297
- * @param array $args Arguments used in condition.
298
- */
299
- protected static function CacheRemove($target, $query, $args)
300
- {
301
- $index = $target . '::' . sprintf($query, $args);
302
- if (!isset(self::$_cache[$index])) {
303
- unset(self::$_cache[$index]);
304
- }
305
- }
306
-
307
- /**
308
- * Clear the cache.
309
- */
310
- protected static function CacheClear()
311
- {
312
- self::$_cache = array();
313
- }
314
-
315
- /**
316
- * Function used in WSAL reporting extension.
317
- * @see WSAL_Adapters_MySQL_ActiveRecord::GetReporting()
318
- * @param int $_siteId site ID
319
- * @param int $_userId user ID
320
- * @param string $_roleName user role
321
- * @param int $_alertCode alert code
322
- * @param timestamp $_startTimestamp from created_on
323
- * @param timestamp $_endTimestamp to created_on
324
- * @return array Report results
325
- */
326
- public function GetReporting($_siteId, $_userId, $_roleName, $_alertCode, $_startTimestamp, $_endTimestamp)
327
- {
328
- return $this->getAdapter()->GetReporting($_siteId, $_userId, $_roleName, $_alertCode, $_startTimestamp, $_endTimestamp);
329
- }
330
-
331
- /**
332
- * Check if the float is IPv4 instead.
333
- * @see WSAL_Models_ActiveRecord::LoadData()
334
- * @param float $ip_address number to check
335
- * @return bool result validation
336
- */
337
- private function is_ip_address($ip_address)
338
- {
339
- if (filter_var($ip_address, FILTER_VALIDATE_IP) !== false) {
340
- return true;
341
- }
342
- return false;
343
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  }
1
  <?php
2
  /**
3
+ * Class: Abstract Active Record
4
+ *
5
+ * Abstract ActiveRecord model is the generic model for any kind
6
+ * of adapter.
7
  *
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Abstract ActiveRecord model is the generic model for any kind
18
  * of adapter.
19
+ *
20
+ * @package Wsal
21
  */
22
+ abstract class WSAL_Models_ActiveRecord {
23
+
24
+ const STATE_UNKNOWN = 'unknown';
25
+ const STATE_CREATED = 'created';
26
+ const STATE_UPDATED = 'updated';
27
+ const STATE_DELETED = 'deleted';
28
+ const STATE_LOADED = 'loaded';
29
+
30
+ /**
31
+ * Data connector
32
+ *
33
+ * @var WSAL_Connector_ConnectorFactory
34
+ */
35
+ protected $connector;
36
+
37
+ /**
38
+ * Record ID.
39
+ *
40
+ * @var boolean
41
+ */
42
+ protected $id = false;
43
+
44
+ /**
45
+ * Adapter Name.
46
+ *
47
+ * @var null
48
+ */
49
+ protected $adapterName = null;
50
+
51
+ /**
52
+ * Use Default Adapter.
53
+ *
54
+ * @var boolean
55
+ */
56
+ protected $useDefaultAdapter = false;
57
+
58
+ /**
59
+ * Record State.
60
+ *
61
+ * @var string
62
+ */
63
+ protected $_state = self::STATE_UNKNOWN;
64
+
65
+ /**
66
+ * Cache.
67
+ *
68
+ * @var array
69
+ */
70
+ protected static $_cache = array();
71
+
72
+ /**
73
+ * Returns this records' fields.
74
+ *
75
+ * @return array
76
+ */
77
+ public function GetFields() {
78
+ if ( ! isset( $this->_column_cache ) ) {
79
+ $this->_column_cache = array();
80
+ foreach ( array_keys( get_object_vars( $this ) ) as $col ) {
81
+ if ( trim( $col ) && '_' != $col[0] ) {
82
+ $this->_column_cache[] = $col;
83
+ }
84
+ }
85
+ }
86
+ return $this->_column_cache;
87
+ }
88
+
89
+ /**
90
+ * Sets the id.
91
+ *
92
+ * @param integer $id - ID.
93
+ */
94
+ public function setId( $id ) {
95
+ $this->id = $id;
96
+ }
97
+
98
+ /**
99
+ * Gets the id.
100
+ *
101
+ * @return integer $id.
102
+ */
103
+ public function getId() {
104
+ return $this->id;
105
+ }
106
+
107
+ /**
108
+ * Method: Constructor.
109
+ *
110
+ * @param array $data - Active data.
111
+ * @throws Exception - Requires adapterName.
112
+ */
113
+ public function __construct( $data = null ) {
114
+ if ( ! $this->adapterName ) {
115
+ throw new Exception( 'Class "' . __CLASS__ . '" requires "adapterName" to be set.' );
116
+ }
117
+ if ( ! is_null( $data ) ) {
118
+ $this->LoadData( $data );
119
+ $this->_state = self::STATE_LOADED;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Gets the connector.
125
+ *
126
+ * @return WSAL_Connector_ConnectorInterface
127
+ */
128
+ protected function getConnector() {
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();
136
+ }
137
+ return $this->connector;
138
+ }
139
+
140
+ /**
141
+ * Gets an adapter for the specified model
142
+ * based on the adapter name.
143
+ *
144
+ * @see WSAL_Connector_ConnectorInterface::getAdapter()
145
+ */
146
+ public function getAdapter() {
147
+ return $this->getConnector()->getAdapter( $this->adapterName );
148
+ }
149
+
150
+ /**
151
+ * Load record from DB.
152
+ *
153
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Load()
154
+ * @param string $cond (Optional) Load condition.
155
+ * @param array $args (Optional) Load condition arguments.
156
+ */
157
+ public function Load( $cond = '%d', $args = array( 1 ) ) {
158
+ $this->_state = self::STATE_UNKNOWN;
159
+
160
+ $data = $this->getAdapter()->Load( $cond, $args );
161
+ if ( ! is_null( $data ) ) {
162
+ $this->LoadData( $data );
163
+ $this->_state = self::STATE_LOADED;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Load object data from variable.
169
+ *
170
+ * @param array|object $data Data array or object.
171
+ * @throws Exception - Unsupported type.
172
+ */
173
+ public function LoadData( $data ) {
174
+ $copy = get_class( $this );
175
+ $copy = new $copy();
176
+ foreach ( (array) $data as $key => $val ) {
177
+ if ( isset( $copy->$key ) ) {
178
+ switch ( true ) {
179
+ case $this->is_ip_address( $val ):
180
+ $this->$key = (string) $val;
181
+ break;
182
+ case is_array( $copy->$key ):
183
+ case is_object( $copy->$key ):
184
+ $json_decoded_val = WSAL_Helpers_DataHelper::JsonDecode( $val );
185
+ $this->$key = ( null == $json_decoded_val ) ? $val : $json_decoded_val;
186
+ break;
187
+ case is_int( $copy->$key ):
188
+ $this->$key = (int) $val;
189
+ break;
190
+ case is_float( $copy->$key ):
191
+ $this->$key = (float) $val;
192
+ break;
193
+ case is_bool( $copy->$key ):
194
+ $this->$key = (bool) $val;
195
+ break;
196
+ case is_string( $copy->$key ):
197
+ $this->$key = (string) $val;
198
+ break;
199
+ default:
200
+ throw new Exception( 'Unsupported type "' . gettype( $copy->$key ) . '"' );
201
+ }
202
+ }
203
+ }
204
+ return $this;
205
+ }
206
+
207
+ /**
208
+ * Save this active record
209
+ *
210
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Save()
211
+ * @return integer|boolean Either the number of modified/inserted rows or false on failure.
212
+ */
213
+ public function Save() {
214
+ $this->_state = self::STATE_UNKNOWN;
215
+
216
+ // Use today's date if not set up.
217
+ if ( is_null( $this->created_on ) ) {
218
+ $this->created_on = $this->GetMicrotime();
219
+ }
220
+ $update_id = $this->getId();
221
+ $result = $this->getAdapter()->Save( $this );
222
+
223
+ if ( false !== $result ) {
224
+ $this->_state = ( ! empty( $update_id )) ? self::STATE_UPDATED : self::STATE_CREATED;
225
+ }
226
+ return $result;
227
+ }
228
+
229
+ /**
230
+ * Deletes this active record.
231
+ *
232
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Delete()
233
+ * @return int|boolean Either the amount of deleted rows or False on error.
234
+ */
235
+ public function Delete() {
236
+ $this->_state = self::STATE_UNKNOWN;
237
+ $result = $this->getAdapter()->Delete( $this );
238
+ if ( false !== $result ) {
239
+ $this->_state = self::STATE_DELETED;
240
+ }
241
+ return $result;
242
+ }
243
+
244
+ /**
245
+ * Count records that matching a condition.
246
+ *
247
+ * @param string $cond - Condition.
248
+ * @param array $args - Arguments.
249
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Count()
250
+ * @return int count
251
+ */
252
+ public function Count( $cond = '%d', $args = array( 1 ) ) {
253
+ $result = $this->getAdapter()->Count( $cond, $args );
254
+ return $result;
255
+ }
256
+
257
+ /**
258
+ * Check state loaded.
259
+ *
260
+ * @return bool
261
+ */
262
+ public function IsLoaded() {
263
+ return self::STATE_LOADED == $this->_state;
264
+ }
265
+
266
+ /**
267
+ * Check state saved.
268
+ *
269
+ * @return bool
270
+ */
271
+ public function IsSaved() {
272
+ return self::STATE_CREATED == $this->_state
273
+ || self::STATE_UPDATED == $this->_state;
274
+ }
275
+
276
+ /**
277
+ * Check state created.
278
+ *
279
+ * @return bool
280
+ */
281
+ public function IsCreated() {
282
+ return self::STATE_CREATED == $this->_state;
283
+ }
284
+
285
+ /**
286
+ * Check state updated.
287
+ *
288
+ * @return bool
289
+ */
290
+ public function IsUpdated() {
291
+ return self::STATE_UPDATED == $this->_state;
292
+ }
293
+
294
+ /**
295
+ * Check if the Record structure is created.
296
+ *
297
+ * @see WSAL_Adapters_MySQL_ActiveRecord::IsInstalled()
298
+ * @return bool
299
+ */
300
+ public function IsInstalled() {
301
+ return $this->getAdapter()->IsInstalled();
302
+ }
303
+
304
+ /**
305
+ * Install the Record structure.
306
+ *
307
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Install()
308
+ */
309
+ public function Install() {
310
+ return $this->getAdapter()->Install();
311
+ }
312
+
313
+ /**
314
+ * Check state deleted.
315
+ *
316
+ * @return bool
317
+ */
318
+ public function IsDeleted() {
319
+ return self::STATE_DELETED == $this->_state;
320
+ }
321
+
322
+ /**
323
+ * Load ActiveRecord from DB or cache.
324
+ *
325
+ * @param string $target ActiveRecord class name.
326
+ * @param string $query Load condition.
327
+ * @param array $args Arguments used in condition.
328
+ * @return WSAL_Models_ActiveRecord
329
+ */
330
+ protected static function CacheLoad( $target, $query, $args ) {
331
+ $index = $target . '::' . vsprintf( $query, $args );
332
+ if ( ! isset( self::$_cache[ $index ] ) ) {
333
+ self::$_cache[ $index ] = new $target();
334
+ self::$_cache[ $index ]->Load( $query, $args );
335
+ }
336
+ return self::$_cache[ $index ];
337
+ }
338
+
339
+ /**
340
+ * Remove ActiveRecord cache.
341
+ *
342
+ * @param string $target ActiveRecord class name.
343
+ * @param string $query Load condition.
344
+ * @param array $args Arguments used in condition.
345
+ */
346
+ protected static function CacheRemove( $target, $query, $args ) {
347
+ $index = $target . '::' . sprintf( $query, $args );
348
+ if ( ! isset( self::$_cache[ $index ] ) ) {
349
+ unset( self::$_cache[ $index ] );
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Clear the cache.
355
+ */
356
+ protected static function CacheClear() {
357
+ self::$_cache = array();
358
+ }
359
+
360
+ /**
361
+ * Function used in WSAL reporting extension.
362
+ *
363
+ * @see WSAL_Adapters_MySQL_ActiveRecord::GetReporting()
364
+ * @param int $_site_id - Site ID.
365
+ * @param int $_user_id - User ID.
366
+ * @param string $_role_name - User role.
367
+ * @param int $_alert_code - Alert code.
368
+ * @param timestamp $_start_timestamp - From created_on.
369
+ * @param timestamp $_end_timestamp - To created_on.
370
+ * @return array - Report results.
371
+ */
372
+ public function GetReporting( $_site_id, $_user_id, $_role_name, $_alert_code, $_start_timestamp, $_end_timestamp ) {
373
+ return $this->getAdapter()->GetReporting( $_site_id, $_user_id, $_role_name, $_alert_code, $_start_timestamp, $_end_timestamp );
374
+ }
375
+
376
+ /**
377
+ * Check if the float is IPv4 instead.
378
+ *
379
+ * @see WSAL_Models_ActiveRecord::LoadData()
380
+ * @param float $ip_address - Number to check.
381
+ * @return bool result validation
382
+ */
383
+ private function is_ip_address( $ip_address ) {
384
+ if ( filter_var( $ip_address, FILTER_VALIDATE_IP ) !== false ) {
385
+ return true;
386
+ }
387
+ return false;
388
+ }
389
  }
classes/Models/Meta.php CHANGED
@@ -1,56 +1,99 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * Metadata model is the model for the Metadata adapter,
6
  * used for save and update the metadata.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  */
8
- class WSAL_Models_Meta extends WSAL_Models_ActiveRecord
9
- {
10
- public $id = 0;
11
- public $occurrence_id = 0;
12
- public $name = '';
13
- public $value = array(); // force mixed type
14
- protected $adapterName = "Meta";
15
-
16
- /**
17
- * Save Metadata into Adapter.
18
- * @see WSAL_Adapters_MySQL_ActiveRecord::Save()
19
- * @return integer|boolean Either the number of modified/inserted rows or false on failure.
20
- */
21
- public function SaveMeta()
22
- {
23
- $this->_state = self::STATE_UNKNOWN;
24
- $updateId = $this->getId();
25
- $result = $this->getAdapter()->Save($this);
26
-
27
- if ($result !== false) {
28
- $this->_state = (!empty($updateId))?self::STATE_UPDATED:self::STATE_CREATED;
29
- }
30
- return $result;
31
- }
32
-
33
- /**
34
- * Update Metadata by name and occurrence_id.
35
- * @see WSAL_Adapters_MySQL_Meta::LoadByNameAndOccurenceId()
36
- * @param string $name meta name
37
- * @param mixed $value meta value
38
- * @param integer $occurrenceId occurrence_id
39
- */
40
- public function UpdateByNameAndOccurenceId($name, $value, $occurrenceId)
41
- {
42
- $meta = $this->getAdapter()->LoadByNameAndOccurenceId($name, $occurrenceId);
43
- if (!empty($meta)) {
44
- $this->id = $meta['id'];
45
- $this->occurrence_id = $meta['occurrence_id'];
46
- $this->name = $meta['name'];
47
- $this->value = $value;
48
- $this->saveMeta();
49
- } else {
50
- $this->occurrence_id = $occurrenceId;
51
- $this->name = $name;
52
- $this->value = $value;
53
- $this->SaveMeta();
54
- }
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
1
  <?php
2
  /**
3
+ * Class: Meta Model Class
4
  *
5
  * Metadata model is the model for the Metadata adapter,
6
  * used for save and update the metadata.
7
+ *
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * Metadata model is the model for the Metadata adapter,
18
+ * used for save and update the metadata.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Models_Meta extends WSAL_Models_ActiveRecord {
23
+
24
+ /**
25
+ * Meta ID.
26
+ *
27
+ * @var integer
28
+ */
29
+ public $id = 0;
30
+
31
+ /**
32
+ * Occurrence ID.
33
+ *
34
+ * @var integer
35
+ */
36
+ public $occurrence_id = 0;
37
+
38
+ /**
39
+ * Meta Name.
40
+ *
41
+ * @var string
42
+ */
43
+ public $name = '';
44
+
45
+ /**
46
+ * Meta Value.
47
+ *
48
+ * @var array
49
+ */
50
+ public $value = array(); // Force mixed type.
51
+
52
+ /**
53
+ * Model Name.
54
+ *
55
+ * @var string
56
+ */
57
+ protected $adapterName = 'Meta';
58
+
59
+ /**
60
+ * Save Metadata into Adapter.
61
+ *
62
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Save()
63
+ * @return integer|boolean Either the number of modified/inserted rows or false on failure.
64
+ */
65
+ public function SaveMeta() {
66
+ $this->_state = self::STATE_UNKNOWN;
67
+ $update_id = $this->getId();
68
+ $result = $this->getAdapter()->Save( $this );
69
+
70
+ if ( false !== $result ) {
71
+ $this->_state = ( ! empty( $update_id )) ? self::STATE_UPDATED : self::STATE_CREATED;
72
+ }
73
+ return $result;
74
+ }
75
+
76
+ /**
77
+ * Update Metadata by name and occurrence_id.
78
+ *
79
+ * @see WSAL_Adapters_MySQL_Meta::LoadByNameAndOccurenceId()
80
+ * @param string $name - Meta name.
81
+ * @param mixed $value - Meta value.
82
+ * @param integer $occurrence_id - Occurrence_id.
83
+ */
84
+ public function UpdateByNameAndOccurenceId( $name, $value, $occurrence_id ) {
85
+ $meta = $this->getAdapter()->LoadByNameAndOccurenceId( $name, $occurrence_id );
86
+ if ( ! empty( $meta ) ) {
87
+ $this->id = $meta['id'];
88
+ $this->occurrence_id = $meta['occurrence_id'];
89
+ $this->name = $meta['name'];
90
+ $this->value = $value;
91
+ $this->saveMeta();
92
+ } else {
93
+ $this->occurrence_id = $occurrence_id;
94
+ $this->name = $name;
95
+ $this->value = $value;
96
+ $this->SaveMeta();
97
+ }
98
+ }
99
  }
classes/Models/Occurrence.php CHANGED
@@ -1,252 +1,294 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * Occurrence model is the model for the Occurrence adapter,
6
  * used for get the alert, set the meta fields, etc.
 
 
7
  */
8
- class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord
9
- {
10
- public $id = 0;
11
- public $site_id = 0;
12
- public $alert_id = 0;
13
- public $created_on = 0.0;
14
- public $is_read = false;
15
- public $is_migrated = false;
16
- protected $adapterName = "Occurrence";
17
-
18
- /**
19
- * Returns the alert related to this occurrence.
20
- * @see WSAL_AlertManager::GetAlert()
21
- * @return WSAL_Alert
22
- */
23
- public function GetAlert()
24
- {
25
- return WpSecurityAuditLog::GetInstance()->alerts->GetAlert($this->alert_id);
26
- }
27
-
28
- /**
29
- * Returns the value of a meta item.
30
- * @see WSAL_Adapters_MySQL_Occurrence::GetNamedMeta()
31
- * @param string $name Name of meta item.
32
- * @param mixed $default Default value returned when meta does not exist.
33
- * @return mixed The value, if meta item does not exist $default returned.
34
- */
35
- public function GetMetaValue($name, $default = array())
36
- {
37
- //get meta adapter
38
- $meta = $this->getAdapter()->GetNamedMeta($this, $name);
39
- return maybe_unserialize($meta['value']);
40
-
41
- //TO DO: re-introduce add is loaded check before running query
42
- //return $meta->IsLoaded() ? $meta->value : $default;
43
- }
44
-
45
- /**
46
- * Sets the value of a meta item (creates or updates meta item).
47
- * @param string $name Meta name.
48
- * @param mixed $value Meta value.
49
- */
50
- public function SetMetaValue($name, $value)
51
- {
52
- if (!empty($value)) {
53
- // get meta adapter
54
- $model = new WSAL_Models_Meta();
55
- $model->occurrence_id = $this->getId();
56
- $model->name = $name;
57
- $model->value = maybe_serialize($value);
58
- $model->SaveMeta();
59
- }
60
- }
61
-
62
- /**
63
- * Update Metadata of this occurrence by name.
64
- * @see WSAL_Models_Meta::UpdateByNameAndOccurenceId()
65
- * @param string $name meta name
66
- * @param mixed $value meta value
67
- */
68
- public function UpdateMetaValue($name, $value)
69
- {
70
- $model = new WSAL_Models_Meta();
71
- $model->UpdateByNameAndOccurenceId($name, $value, $this->getId());
72
- }
73
-
74
- /**
75
- * Returns a key-value pair of meta data.
76
- * @see WSAL_Adapters_MySQL_Occurrence::GetMultiMeta()
77
- * @return array
78
- */
79
- public function GetMetaArray()
80
- {
81
- $result = array();
82
- $metas = $this->getAdapter()->GetMultiMeta($this);
83
- foreach ($metas as $meta) {
84
- $result[$meta->name] = maybe_unserialize($meta->value);
85
- }
86
- return $result;
87
- }
88
-
89
- /**
90
- * Creates or updates all meta data passed as an array of meta-key/meta-value pairs.
91
- * @param array $data New meta data.
92
- */
93
- public function SetMeta($data)
94
- {
95
- foreach ((array)$data as $key => $val) {
96
- $this->SetMetaValue($key, $val);
97
- }
98
- }
99
-
100
- /**
101
- * Gets alert message.
102
- * @see WSAL_Alert::GetMessage()
103
- * @param callable|null $metaFormatter (Optional) Meta formatter callback.
104
- * @return string Full-formatted message.
105
- */
106
- public function GetMessage($metaFormatter = null)
107
- {
108
- if (!isset($this->_cachedmessage)) {
109
- // get correct message entry
110
- if ($this->is_migrated) {
111
- $this->_cachedmessage = $this->GetMetaValue('MigratedMesg', false);
112
- }
113
- if (!$this->is_migrated || !$this->_cachedmessage) {
114
- $this->_cachedmessage = $this->GetAlert()->mesg;
115
- }
116
- // fill variables in message
117
- $this->_cachedmessage = $this->GetAlert()->GetMessage($this->GetMetaArray(), $metaFormatter, $this->_cachedmessage);
118
- }
119
- return $this->_cachedmessage;
120
- }
121
-
122
- /**
123
- * Delete occurrence as well as associated meta data.
124
- * @see WSAL_Adapters_ActiveRecordInterface::Delete()
125
- * @return boolean True on success, false on failure.
126
- */
127
- public function Delete()
128
- {
129
- foreach ($this->getAdapter()->GetMeta() as $meta) {
130
- $meta->Delete();
131
- }
132
- return parent::Delete();
133
- }
134
-
135
- /**
136
- * Gets the username.
137
- * @see WSAL_Adapters_MySQL_Occurrence::GetFirstNamedMeta()
138
- * @return string User's username.
139
- */
140
- public function GetUsername()
141
- {
142
- $meta = $this->getAdapter()->GetFirstNamedMeta($this, array('Username', 'CurrentUserID'));
143
- if ($meta) {
144
- switch (true) {
145
- case $meta->name == 'Username':
146
- return $meta->value;
147
- case $meta->name == 'CurrentUserID':
148
- return ($data = get_userdata($meta->value)) ? $data->user_login : null;
149
- }
150
- }
151
- return null;
152
- }
153
-
154
- /**
155
- * Gets the Client IP.
156
- * @return string IP address of request.
157
- */
158
- public function GetSourceIP()
159
- {
160
- return $this->GetMetaValue('ClientIP', '');
161
- }
162
-
163
- /**
164
- * Gets if there are other IPs.
165
- * @return string IP address of request (from proxies etc).
166
- */
167
- public function GetOtherIPs()
168
- {
169
- $result = array();
170
- $data = (array)$this->GetMetaValue('OtherIPs', array());
171
- foreach ($data as $ips) {
172
- foreach ($ips as $ip) {
173
- $result[] = $ip;
174
- }
175
- }
176
- return array_unique($result);
177
- }
178
-
179
- /**
180
- * Gets user roles.
181
- * @return array Array of user roles.
182
- */
183
- public function GetUserRoles()
184
- {
185
- return $this->GetMetaValue('CurrentUserRoles', array());
186
- }
187
-
188
- /**
189
- * @return float Number of seconds (and microseconds as fraction) since unix Day 0.
190
- * @todo This needs some caching.
191
- */
192
- protected function GetMicrotime()
193
- {
194
- return microtime(true);// + get_option('gmt_offset') * HOUR_IN_SECONDS;
195
- }
196
-
197
- /**
198
- * Finds occurences of the same type by IP and Username within specified time frame.
199
- * @param string $ipAddress
200
- * @param string $username
201
- * @param int $alertId Alert type we are lookign for
202
- * @param int $siteId
203
- * @param $startTime mktime
204
- * @param $endTime mktime
205
- * @return WSAL_Occurrence[]
206
- */
207
- public function CheckKnownUsers($args = array())
208
- {
209
- return $this->getAdapter()->CheckKnownUsers($args);
210
- }
211
-
212
- /**
213
- * Finds occurences of the same type by IP within specified time frame.
214
- * @param string $ipAddress
215
- * @param int $alertId Alert type we are lookign for
216
- * @param int $siteId
217
- * @param $startTime mktime
218
- * @param $endTime mktime
219
- * @return WSAL_Occurrence[]
220
- */
221
- public function CheckUnKnownUsers($args = array())
222
- {
223
- return $this->getAdapter()->CheckUnKnownUsers($args);
224
- }
225
-
226
- /**
227
- * Gets occurrence by Post_id
228
- * @see WSAL_Adapters_MySQL_Occurrence::GetByPostID()
229
- * @param integer $post_id
230
- * @return WSAL_Occurrence[]
231
- */
232
- public function GetByPostID($post_id)
233
- {
234
- return $this->getAdapter()->GetByPostID($post_id);
235
- }
236
-
237
- /**
238
- * Gets occurences of the same type by IP within specified time frame.
239
- * @see WSAL_Adapters_MySQL_Occurrence::CheckAlert404()
240
- * @param string $ipAddress
241
- * @param string $username
242
- * @param int $alertId Alert type we are lookign for
243
- * @param int $siteId
244
- * @param $startTime mktime
245
- * @param $endTime mktime
246
- * @return WSAL_Occurrence[]
247
- */
248
- public function CheckAlert404($args = array())
249
- {
250
- return $this->getAdapter()->CheckAlert404($args);
251
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  }
1
  <?php
2
  /**
3
+ * Class: Occurrence Model Class
4
  *
5
  * Occurrence model is the model for the Occurrence adapter,
6
  * used for get the alert, set the meta fields, etc.
7
+ *
8
+ * @package Wsal
9
  */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * Occurrence model is the model for the Occurrence adapter,
18
+ * used for get the alert, set the meta fields, etc.
19
+ *
20
+ * @package Wsal
21
+ */
22
+ class WSAL_Models_Occurrence extends WSAL_Models_ActiveRecord {
23
+
24
+ /**
25
+ * Occurrence ID.
26
+ *
27
+ * @var integer
28
+ */
29
+ public $id = 0;
30
+
31
+ /**
32
+ * Site ID.
33
+ *
34
+ * @var integer
35
+ */
36
+ public $site_id = 0;
37
+
38
+ /**
39
+ * Alert ID.
40
+ *
41
+ * @var integer
42
+ */
43
+ public $alert_id = 0;
44
+
45
+ /**
46
+ * Created On.
47
+ *
48
+ * @var string
49
+ */
50
+ public $created_on = 0.0;
51
+
52
+ /**
53
+ * Is read.
54
+ *
55
+ * @var bool
56
+ */
57
+ public $is_read = false;
58
+
59
+ /**
60
+ * Is migrated.
61
+ *
62
+ * @var bool
63
+ */
64
+ public $is_migrated = false;
65
+
66
+ /**
67
+ * Model Name.
68
+ *
69
+ * @var string
70
+ */
71
+ protected $adapterName = 'Occurrence';
72
+
73
+ /**
74
+ * Returns the alert related to this occurrence.
75
+ *
76
+ * @see WSAL_AlertManager::GetAlert()
77
+ * @return WSAL_Alert
78
+ */
79
+ public function GetAlert() {
80
+ return WpSecurityAuditLog::GetInstance()->alerts->GetAlert( $this->alert_id );
81
+ }
82
+
83
+ /**
84
+ * Returns the value of a meta item.
85
+ *
86
+ * @see WSAL_Adapters_MySQL_Occurrence::GetNamedMeta()
87
+ * @param string $name - Name of meta item.
88
+ * @param mixed $default - Default value returned when meta does not exist.
89
+ * @return mixed The value, if meta item does not exist $default returned.
90
+ */
91
+ public function GetMetaValue( $name, $default = array() ) {
92
+ // Get meta adapter.
93
+ $meta = $this->getAdapter()->GetNamedMeta( $this, $name );
94
+ return maybe_unserialize( $meta['value'] );
95
+
96
+ // TO DO: re-introduce add is loaded check before running query
97
+ // return $meta->IsLoaded() ? $meta->value : $default;
98
+ }
99
+
100
+ /**
101
+ * Sets the value of a meta item (creates or updates meta item).
102
+ *
103
+ * @param string $name - Meta name.
104
+ * @param mixed $value - Meta value.
105
+ */
106
+ public function SetMetaValue( $name, $value ) {
107
+ if ( ! empty( $value ) ) {
108
+ // Get meta adapter.
109
+ $model = new WSAL_Models_Meta();
110
+ $model->occurrence_id = $this->getId();
111
+ $model->name = $name;
112
+ $model->value = maybe_serialize( $value );
113
+ $model->SaveMeta();
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Update Metadata of this occurrence by name.
119
+ *
120
+ * @see WSAL_Models_Meta::UpdateByNameAndOccurenceId()
121
+ * @param string $name - Meta name.
122
+ * @param mixed $value - Meta value.
123
+ */
124
+ public function UpdateMetaValue( $name, $value ) {
125
+ $model = new WSAL_Models_Meta();
126
+ $model->UpdateByNameAndOccurenceId( $name, $value, $this->getId() );
127
+ }
128
+
129
+ /**
130
+ * Returns a key-value pair of meta data.
131
+ *
132
+ * @see WSAL_Adapters_MySQL_Occurrence::GetMultiMeta()
133
+ * @return array
134
+ */
135
+ public function GetMetaArray() {
136
+ $result = array();
137
+ $metas = $this->getAdapter()->GetMultiMeta( $this );
138
+ foreach ( $metas as $meta ) {
139
+ $result[ $meta->name ] = maybe_unserialize( $meta->value );
140
+ }
141
+ return $result;
142
+ }
143
+
144
+ /**
145
+ * Creates or updates all meta data passed as an array of meta-key/meta-value pairs.
146
+ *
147
+ * @param array $data - New meta data.
148
+ */
149
+ public function SetMeta( $data ) {
150
+ foreach ( (array) $data as $key => $val ) {
151
+ $this->SetMetaValue( $key, $val );
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Gets alert message.
157
+ *
158
+ * @see WSAL_Alert::GetMessage()
159
+ * @param callable|null $meta_formatter (Optional) Meta formatter callback.
160
+ * @return string Full-formatted message.
161
+ */
162
+ public function GetMessage( $meta_formatter = null ) {
163
+ if ( ! isset( $this->_cachedmessage ) ) {
164
+ // Get correct message entry.
165
+ if ( $this->is_migrated ) {
166
+ $this->_cachedmessage = $this->GetMetaValue( 'MigratedMesg', false );
167
+ }
168
+ if ( ! $this->is_migrated || ! $this->_cachedmessage ) {
169
+ $this->_cachedmessage = $this->GetAlert()->mesg;
170
+ }
171
+ // Fill variables in message.
172
+ $this->_cachedmessage = $this->GetAlert()->GetMessage( $this->GetMetaArray(), $meta_formatter, $this->_cachedmessage );
173
+ }
174
+ return $this->_cachedmessage;
175
+ }
176
+
177
+ /**
178
+ * Delete occurrence as well as associated meta data.
179
+ *
180
+ * @see WSAL_Adapters_ActiveRecordInterface::Delete()
181
+ * @return boolean True on success, false on failure.
182
+ */
183
+ public function Delete() {
184
+ foreach ( $this->getAdapter()->GetMeta() as $meta ) {
185
+ $meta->Delete();
186
+ }
187
+ return parent::Delete();
188
+ }
189
+
190
+ /**
191
+ * Gets the username.
192
+ *
193
+ * @see WSAL_Adapters_MySQL_Occurrence::GetFirstNamedMeta()
194
+ * @return string User's username.
195
+ */
196
+ public function GetUsername() {
197
+ $meta = $this->getAdapter()->GetFirstNamedMeta( $this, array( 'Username', 'CurrentUserID' ) );
198
+ if ( $meta ) {
199
+ switch ( true ) {
200
+ case 'Username' == $meta->name:
201
+ return $meta->value;
202
+ case 'CurrentUserID' == $meta->name:
203
+ return ($data = get_userdata( $meta->value )) ? $data->user_login : null;
204
+ }
205
+ }
206
+ return null;
207
+ }
208
+
209
+ /**
210
+ * Gets the Client IP.
211
+ *
212
+ * @return string IP address of request.
213
+ */
214
+ public function GetSourceIP() {
215
+ return $this->GetMetaValue( 'ClientIP', '' );
216
+ }
217
+
218
+ /**
219
+ * Gets if there are other IPs.
220
+ *
221
+ * @return string IP address of request (from proxies etc).
222
+ */
223
+ public function GetOtherIPs() {
224
+ $result = array();
225
+ $data = (array) $this->GetMetaValue( 'OtherIPs', array() );
226
+ foreach ( $data as $ips ) {
227
+ foreach ( $ips as $ip ) {
228
+ $result[] = $ip;
229
+ }
230
+ }
231
+ return array_unique( $result );
232
+ }
233
+
234
+ /**
235
+ * Gets user roles.
236
+ *
237
+ * @return array Array of user roles.
238
+ */
239
+ public function GetUserRoles() {
240
+ return $this->GetMetaValue( 'CurrentUserRoles', array() );
241
+ }
242
+
243
+ /**
244
+ * Method: Get Microtime.
245
+ *
246
+ * @return float - Number of seconds (and microseconds as fraction) since unix Day 0.
247
+ * @todo This needs some caching.
248
+ */
249
+ protected function GetMicrotime() {
250
+ return microtime( true );// + get_option('gmt_offset') * HOUR_IN_SECONDS;
251
+ }
252
+
253
+ /**
254
+ * Finds occurences of the same type by IP and Username within specified time frame.
255
+ *
256
+ * @param array $args - Query args.
257
+ * @return WSAL_Occurrence[]
258
+ */
259
+ public function CheckKnownUsers( $args = array() ) {
260
+ return $this->getAdapter()->CheckKnownUsers( $args );
261
+ }
262
+
263
+ /**
264
+ * Finds occurences of the same type by IP within specified time frame.
265
+ *
266
+ * @param array $args - Query args.
267
+ * @return WSAL_Occurrence[]
268
+ */
269
+ public function CheckUnKnownUsers( $args = array() ) {
270
+ return $this->getAdapter()->CheckUnKnownUsers( $args );
271
+ }
272
+
273
+ /**
274
+ * Gets occurrence by Post_id
275
+ *
276
+ * @see WSAL_Adapters_MySQL_Occurrence::GetByPostID()
277
+ * @param integer $post_id - Post ID.
278
+ * @return WSAL_Occurrence[]
279
+ */
280
+ public function GetByPostID( $post_id ) {
281
+ return $this->getAdapter()->GetByPostID( $post_id );
282
+ }
283
+
284
+ /**
285
+ * Gets occurences of the same type by IP within specified time frame.
286
+ *
287
+ * @see WSAL_Adapters_MySQL_Occurrence::CheckAlert404()
288
+ * @param array $args - Query args.
289
+ * @return WSAL_Occurrence[]
290
+ */
291
+ public function CheckAlert404( $args = array() ) {
292
+ return $this->getAdapter()->CheckAlert404( $args );
293
+ }
294
  }
classes/Models/OccurrenceQuery.php CHANGED
@@ -1,42 +1,62 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
5
  * OccurrenceQuery model adds or clears arguments in the Query model.
 
 
6
  */
7
- class WSAL_Models_OccurrenceQuery extends WSAL_Models_Query
8
- {
9
- protected $arguments = array();
 
 
 
 
 
10
 
11
- /**
12
- * Sets arguments.
13
- * @param string $field name field
14
- * @param mixed $value value
15
- * @return self
16
- */
17
- public function addArgument($field, $value)
18
- {
19
- $this->arguments[$field] = $value;
20
- return $this;
21
- }
22
 
23
- /**
24
- * Resets arguments.
25
- * @return self
26
- */
27
- public function clearArguments()
28
- {
29
- $this->arguments = array();
30
- return $this;
31
- }
32
 
33
- public function __construct()
34
- {
35
- parent::__construct();
 
 
 
 
36
 
37
- //TO DO: Consider if Get Table is the right method to call given that this is mysql specific
38
- $this->addFrom(
39
- $this->getConnector()->getAdapter("Occurrence")->GetTable()
40
- );
41
- }
42
  }
1
  <?php
2
  /**
3
+ * Class: Occurrence Query Class
4
+ *
5
+ * OccurrenceQuery model adds or clears arguments in the Query model.
6
  *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * OccurrenceQuery model adds or clears arguments in the Query model.
17
+ *
18
+ * @package Wsal
19
  */
20
+ class WSAL_Models_OccurrenceQuery extends WSAL_Models_Query {
21
+
22
+ /**
23
+ * Query Arguments.
24
+ *
25
+ * @var array
26
+ */
27
+ protected $arguments = array();
28
 
29
+ /**
30
+ * Sets arguments.
31
+ *
32
+ * @param string $field - Name field.
33
+ * @param mixed $value - Value.
34
+ * @return self
35
+ */
36
+ public function addArgument( $field, $value ) {
37
+ $this->arguments[ $field ] = $value;
38
+ return $this;
39
+ }
40
 
41
+ /**
42
+ * Resets arguments.
43
+ *
44
+ * @return self
45
+ */
46
+ public function clearArguments() {
47
+ $this->arguments = array();
48
+ return $this;
49
+ }
50
 
51
+ /**
52
+ * Method: Constructor
53
+ *
54
+ * @since 1.0.0
55
+ */
56
+ public function __construct() {
57
+ parent::__construct();
58
 
59
+ // TO DO: Consider if Get Table is the right method to call given that this is mysql specific.
60
+ $this->addFrom( $this->getConnector()->getAdapter( 'Occurrence' )->GetTable() );
61
+ }
 
 
62
  }
classes/Models/Option.php CHANGED
@@ -1,128 +1,167 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
4
- * Wordpress options are always loaded from the default wordpress database.
 
 
 
 
 
 
 
 
5
  *
6
  * Option Model gets and sets the options of the wsal_options table in the database.
 
 
7
  */
8
- class WSAL_Models_Option extends WSAL_Models_ActiveRecord
9
- {
10
- public $id = '';
11
- public $option_name = '';
12
- public $option_value = '';
13
- protected $adapterName = "Option";
14
-
15
- /**
16
- * Options are always stored in WPDB. This setting ensures that
17
- */
18
- protected $useDefaultAdapter = true;
19
-
20
- /**
21
- * Sets Option record.
22
- * @param string $name option name
23
- * @param mixed $value option value
24
- */
25
- public function SetOptionValue($name, $value)
26
- {
27
- $option = $this->getAdapter()->GetNamedOption($name);
28
- $this->id = $option['id'];
29
- $this->option_name = $name;
30
- // Serialize if $value is array or object
31
- $value = maybe_serialize($value);
32
- $this->option_value = $value;
33
- return $this->Save();
34
- }
35
-
36
- /**
37
- * Gets Option record.
38
- * @param string $name option name
39
- * @param mixed $default (Optional) default value
40
- * @return mixed option value
41
- */
42
- public function GetOptionValue($name, $default = array())
43
- {
44
- $option = $this->getAdapter()->GetNamedOption($name);
45
- $this->option_value = (!empty($option)) ? $option['option_value'] : null;
46
- if (!empty($this->option_value)) {
47
- $this->_state = self::STATE_LOADED;
48
- }
49
- // Unerialize if $value is array or object
50
- $this->option_value = maybe_unserialize($this->option_value);
51
- return $this->IsLoaded() ? $this->option_value : $default;
52
- }
53
-
54
- /**
55
- * Save Option record.
56
- * @see WSAL_Adapters_MySQL_ActiveRecord::Save()
57
- * @return integer|boolean Either the number of modified/inserted rows or false on failure.
58
- */
59
- public function Save()
60
- {
61
- $this->_state = self::STATE_UNKNOWN;
62
-
63
- $updateId = $this->getId();
64
- $result = $this->getAdapter()->Save($this);
65
-
66
- if ($result !== false) {
67
- $this->_state = (!empty($updateId))?self::STATE_UPDATED:self::STATE_CREATED;
68
- }
69
- return $result;
70
- }
71
-
72
- /**
73
- * Get options by prefix (notifications stored in json format).
74
- * @see WSAL_Adapters_MySQL_Option::GetNotificationsSetting()
75
- * @param string $opt_prefix prefix
76
- * @return array|null options
77
- */
78
- public function GetNotificationsSetting($opt_prefix)
79
- {
80
- return $this->getAdapter()->GetNotificationsSetting($opt_prefix);
81
- }
82
-
83
- /**
84
- * Get option by id (notifications stored in json format).
85
- * @see WSAL_Adapters_MySQL_Option::GetNotification()
86
- * @param int $id option ID
87
- * @return string|null option
88
- */
89
- public function GetNotification($id)
90
- {
91
- return $this->LoadData(
92
- $this->getAdapter()->GetNotification($id)
93
- );
94
- }
95
-
96
- /**
97
- * Delete option by name.
98
- * @see WSAL_Adapters_MySQL_Option::DeleteByName()
99
- * @param string $name option_name
100
- * @return boolean
101
- */
102
- public function DeleteByName($name)
103
- {
104
- return $this->getAdapter()->DeleteByName($name);
105
- }
106
-
107
- /**
108
- * Delete options start with prefix.
109
- * @see WSAL_Adapters_MySQL_Option::DeleteByPrefix()
110
- * @param string $opt_prefix prefix
111
- * @return boolean
112
- */
113
- public function DeleteByPrefix($opt_prefix)
114
- {
115
- return $this->getAdapter()->DeleteByPrefix($opt_prefix);
116
- }
117
-
118
- /**
119
- * Number of options start with prefix.
120
- * @see WSAL_Adapters_MySQL_Option::CountNotifications()
121
- * @param string $opt_prefix prefix
122
- * @return integer Indicates the number of items.
123
- */
124
- public function CountNotifications($opt_prefix)
125
- {
126
- return $this->getAdapter()->CountNotifications($opt_prefix);
127
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  }
1
  <?php
2
  /**
3
+ * Class: Options Model Class
4
+ *
5
+ * Option Model gets and sets the options of the wsal_options table in the database.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * WordPress options are always loaded from the default WordPress database.
17
  *
18
  * Option Model gets and sets the options of the wsal_options table in the database.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Models_Option extends WSAL_Models_ActiveRecord {
23
+
24
+ /**
25
+ * Option ID.
26
+ *
27
+ * @var int
28
+ */
29
+ public $id = '';
30
+
31
+ /**
32
+ * Option Name.
33
+ *
34
+ * @var string
35
+ */
36
+ public $option_name = '';
37
+
38
+ /**
39
+ * Option Value.
40
+ *
41
+ * @var string
42
+ */
43
+ public $option_value = '';
44
+
45
+ /**
46
+ * Model Name.
47
+ *
48
+ * @var string
49
+ */
50
+ protected $adapterName = 'Option';
51
+
52
+ /**
53
+ * Options are always stored in WPDB. This setting ensures that.
54
+ *
55
+ * @var bool
56
+ */
57
+ protected $useDefaultAdapter = true;
58
+
59
+ /**
60
+ * Sets Option record.
61
+ *
62
+ * @param string $name - Option name.
63
+ * @param mixed $value - Option value.
64
+ */
65
+ public function SetOptionValue( $name, $value ) {
66
+ $option = $this->getAdapter()->GetNamedOption( $name );
67
+ $this->id = $option['id'];
68
+ $this->option_name = $name;
69
+ // Serialize if $value is array or object.
70
+ $value = maybe_serialize( $value );
71
+ $this->option_value = $value;
72
+ return $this->Save();
73
+ }
74
+
75
+ /**
76
+ * Gets Option record.
77
+ *
78
+ * @param string $name - Option name.
79
+ * @param mixed $default - (Optional) Default value.
80
+ * @return mixed option value
81
+ */
82
+ public function GetOptionValue( $name, $default = array() ) {
83
+ $option = $this->getAdapter()->GetNamedOption( $name );
84
+ $this->option_value = ( ! empty( $option )) ? $option['option_value'] : null;
85
+ if ( ! empty( $this->option_value ) ) {
86
+ $this->_state = self::STATE_LOADED;
87
+ }
88
+ // Unserialize if $value is array or object.
89
+ $this->option_value = maybe_unserialize( $this->option_value );
90
+ return $this->IsLoaded() ? $this->option_value : $default;
91
+ }
92
+
93
+ /**
94
+ * Save Option record.
95
+ *
96
+ * @see WSAL_Adapters_MySQL_ActiveRecord::Save()
97
+ * @return integer|boolean Either the number of modified/inserted rows or false on failure.
98
+ */
99
+ public function Save() {
100
+ $this->_state = self::STATE_UNKNOWN;
101
+
102
+ $update_id = $this->getId();
103
+ $result = $this->getAdapter()->Save( $this );
104
+
105
+ if ( false !== $result ) {
106
+ $this->_state = ( ! empty( $update_id )) ? self::STATE_UPDATED : self::STATE_CREATED;
107
+ }
108
+ return $result;
109
+ }
110
+
111
+ /**
112
+ * Get options by prefix (notifications stored in json format).
113
+ *
114
+ * @see WSAL_Adapters_MySQL_Option::GetNotificationsSetting()
115
+ * @param string $opt_prefix - Prefix.
116
+ * @return array|null options
117
+ */
118
+ public function GetNotificationsSetting( $opt_prefix ) {
119
+ return $this->getAdapter()->GetNotificationsSetting( $opt_prefix );
120
+ }
121
+
122
+ /**
123
+ * Get option by id (notifications stored in json format).
124
+ *
125
+ * @see WSAL_Adapters_MySQL_Option::GetNotification()
126
+ * @param int $id - Option ID.
127
+ * @return string|null option
128
+ */
129
+ public function GetNotification( $id ) {
130
+ return $this->LoadData(
131
+ $this->getAdapter()->GetNotification( $id )
132
+ );
133
+ }
134
+
135
+ /**
136
+ * Delete option by name.
137
+ *
138
+ * @see WSAL_Adapters_MySQL_Option::DeleteByName()
139
+ * @param string $name - Option name.
140
+ * @return boolean
141
+ */
142
+ public function DeleteByName( $name ) {
143
+ return $this->getAdapter()->DeleteByName( $name );
144
+ }
145
+
146
+ /**
147
+ * Delete options start with prefix.
148
+ *
149
+ * @see WSAL_Adapters_MySQL_Option::DeleteByPrefix()
150
+ * @param string $opt_prefix - Prefix.
151
+ * @return boolean
152
+ */
153
+ public function DeleteByPrefix( $opt_prefix ) {
154
+ return $this->getAdapter()->DeleteByPrefix( $opt_prefix );
155
+ }
156
+
157
+ /**
158
+ * Number of options start with prefix.
159
+ *
160
+ * @see WSAL_Adapters_MySQL_Option::CountNotifications()
161
+ * @param string $opt_prefix - Prefix.
162
+ * @return integer Indicates the number of items.
163
+ */
164
+ public function CountNotifications( $opt_prefix ) {
165
+ return $this->getAdapter()->CountNotifications( $opt_prefix );
166
+ }
167
  }
classes/Models/Query.php CHANGED
@@ -1,271 +1,342 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * Query Class.
5
  *
6
  * Query model is the class for all the query conditions.
 
 
7
  */
8
- class WSAL_Models_Query
9
- {
10
- protected $columns = array();
11
- protected $conditions = array();
12
- protected $orderBy = array();
13
- protected $offset = null;
14
- protected $limit = null;
15
- protected $from = array();
16
- protected $meta_join = false;
17
- protected $searchCondition = null;
18
- protected $useDefaultAdapter = false;
19
-
20
- public function __construct()
21
- {
22
- }
23
-
24
- /**
25
- * Initialize a connector singleton.
26
- * @return WSAL_Connector_ConnectorInterface
27
- */
28
- public function getConnector()
29
- {
30
- if (!empty($this->connector)) {
31
- return $this->connector;
32
- }
33
- if ($this->useDefaultAdapter) {
34
- $this->connector = WSAL_Connector_ConnectorFactory::GetDefaultConnector();
35
- } else {
36
- $this->connector = WSAL_Connector_ConnectorFactory::GetConnector();
37
- }
38
- return $this->connector;
39
- }
40
-
41
- /**
42
- * Gets the adapter.
43
- * @return WSAL_Adapters_MySQL_Query
44
- */
45
- public function getAdapter()
46
- {
47
- return $this->getConnector()->getAdapter('Query');
48
- }
49
-
50
- /**
51
- * Add a column.
52
- * @param mixed $column column value
53
- * @return self
54
- */
55
- public function addColumn($column)
56
- {
57
- $this->columns[] = $column;
58
- return $this;
59
- }
60
-
61
- /**
62
- * Clear all columns.
63
- * @return self
64
- */
65
- public function clearColumns()
66
- {
67
- $this->columns = array();
68
- return $this;
69
- }
70
-
71
- /**
72
- * Get columns.
73
- * @return array $columns
74
- */
75
- public function getColumns()
76
- {
77
- return $this->columns;
78
- }
79
-
80
- /**
81
- * Set all columns.
82
- * @param array $columns columns values
83
- * @return self
84
- */
85
- public function setColumns($columns)
86
- {
87
- $this->columns = $columns;
88
- return $this;
89
- }
90
-
91
- /**
92
- * Add conditions.
93
- * @param string $field condition field
94
- * @param mixed $value condition value
95
- * @return self
96
- */
97
- public function addCondition($field, $value)
98
- {
99
- $this->conditions[$field] = $value;
100
- return $this;
101
- }
102
-
103
- /**
104
- * Add OR condition.
105
- * @param array $aConditions multi conditions
106
- */
107
- public function addORCondition($aConditions)
108
- {
109
- $this->conditions[] = $aConditions;
110
- }
111
-
112
- /**
113
- * Clear all conditions.
114
- * @return self
115
- */
116
- public function clearConditions()
117
- {
118
- $this->conditions = array();
119
- return $this;
120
- }
121
-
122
- /**
123
- * Get all conditions.
124
- * @return array $conditions
125
- */
126
- public function getConditions()
127
- {
128
- return $this->conditions;
129
- }
130
-
131
- /**
132
- * Add order by.
133
- * @param string $field field name
134
- * @param boolean $isDescending (Optional) ascending/descending
135
- * @return self
136
- */
137
- public function addOrderBy($field, $isDescending = false)
138
- {
139
- $order = ($isDescending) ? 'DESC' : 'ASC';
140
- $this->orderBy[$field] = $order;
141
- return $this;
142
- }
143
-
144
- /**
145
- * Clear order by.
146
- * @return self
147
- */
148
- public function clearOrderBy()
149
- {
150
- $this->orderBy = array();
151
- return $this;
152
- }
153
-
154
- /**
155
- * Get order by.
156
- * @return array $orderBy
157
- */
158
- public function getOrderBy()
159
- {
160
- return $this->orderBy;
161
- }
162
-
163
- /**
164
- * Add from.
165
- * @param string $fromDataSet data set
166
- * @return self
167
- */
168
- public function addFrom($fromDataSet)
169
- {
170
- $this->from[] = $fromDataSet;
171
- return $this;
172
- }
173
-
174
- /**
175
- * Reset from.
176
- * @return self
177
- */
178
- public function clearFrom()
179
- {
180
- $this->from = array();
181
- return $this;
182
- }
183
-
184
- /**
185
- * Get from.
186
- * @return string $from data set
187
- */
188
- public function getFrom()
189
- {
190
- return $this->from;
191
- }
192
-
193
- /**
194
- * Gets the value of limit.
195
- * @return mixed
196
- */
197
- public function getLimit()
198
- {
199
- return $this->limit;
200
- }
201
-
202
- /**
203
- * Sets the value of limit.
204
- * @param mixed $limit the limit
205
- * @return self
206
- */
207
- public function setLimit($limit)
208
- {
209
- $this->limit = $limit;
210
- return $this;
211
- }
212
-
213
- /**
214
- * Gets the value of offset.
215
- * @return mixed
216
- */
217
- public function getOffset()
218
- {
219
- return $this->offset;
220
- }
221
-
222
- /**
223
- * Sets the value of offset.
224
- * @param mixed $offset the offset
225
- * @return self
226
- */
227
- public function setOffset($offset)
228
- {
229
- $this->offset = $offset;
230
- return $this;
231
- }
232
-
233
- /**
234
- * Adds condition.
235
- * @param mixed $value condition
236
- * @return self
237
- */
238
- public function addSearchCondition($value)
239
- {
240
- $this->searchCondition = $value;
241
- return $this;
242
- }
243
-
244
- /**
245
- * Gets condition.
246
- * @return self
247
- */
248
- public function getSearchCondition()
249
- {
250
- return $this->searchCondition;
251
- }
252
-
253
- /**
254
- * Check meta join.
255
- * @return boolean
256
- */
257
- public function hasMetaJoin()
258
- {
259
- return $this->meta_join;
260
- }
261
-
262
- /**
263
- * Adds meta join.
264
- * @return self
265
- */
266
- public function addMetaJoin()
267
- {
268
- $this->meta_join = true;
269
- return $this;
270
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  }
1
  <?php
2
  /**
3
+ * Class: Query Model Class
4
+ *
5
+ * Query model is the class for all the query conditions.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * Query Class.
17
  *
18
  * Query model is the class for all the query conditions.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Models_Query {
23
+
24
+ /**
25
+ * Table Column.
26
+ *
27
+ * @var array
28
+ */
29
+ protected $columns = array();
30
+
31
+ /**
32
+ * Query Conditions.
33
+ *
34
+ * @var array
35
+ */
36
+ protected $conditions = array();
37
+
38
+ /**
39
+ * Order By.
40
+ *
41
+ * @var array
42
+ */
43
+ protected $orderBy = array();
44
+
45
+ /**
46
+ * Offset.
47
+ *
48
+ * @var mixed
49
+ */
50
+ protected $offset = null;
51
+
52
+ /**
53
+ * Limit.
54
+ *
55
+ * @var mixed
56
+ */
57
+ protected $limit = null;
58
+
59
+ /**
60
+ * From.
61
+ *
62
+ * @var array
63
+ */
64
+ protected $from = array();
65
+
66
+ /**
67
+ * Meta Join.
68
+ *
69
+ * @var bool
70
+ */
71
+ protected $meta_join = false;
72
+
73
+ /**
74
+ * Search Condition.
75
+ *
76
+ * @var mixed
77
+ */
78
+ protected $searchCondition = null;
79
+
80
+ /**
81
+ * Use Default Adapter.
82
+ *
83
+ * @var bool
84
+ */
85
+ protected $useDefaultAdapter = false;
86
+
87
+ /**
88
+ * Method: Constructor.
89
+ *
90
+ * @since 1.0.0
91
+ */
92
+ public function __construct() {
93
+ }
94
+
95
+ /**
96
+ * Initialize a connector singleton.
97
+ *
98
+ * @return WSAL_Connector_ConnectorInterface
99
+ */
100
+ public function getConnector() {
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();
108
+ }
109
+ return $this->connector;
110
+ }
111
+
112
+ /**
113
+ * Gets the adapter.
114
+ *
115
+ * @return WSAL_Adapters_MySQL_Query
116
+ */
117
+ public function getAdapter() {
118
+ return $this->getConnector()->getAdapter( 'Query' );
119
+ }
120
+
121
+ /**
122
+ * Add a column.
123
+ *
124
+ * @param mixed $column - Column value.
125
+ * @return self
126
+ */
127
+ public function addColumn( $column ) {
128
+ $this->columns[] = $column;
129
+ return $this;
130
+ }
131
+
132
+ /**
133
+ * Clear all columns.
134
+ *
135
+ * @return self
136
+ */
137
+ public function clearColumns() {
138
+ $this->columns = array();
139
+ return $this;
140
+ }
141
+
142
+ /**
143
+ * Get columns.
144
+ *
145
+ * @return array $columns
146
+ */
147
+ public function getColumns() {
148
+ return $this->columns;
149
+ }
150
+
151
+ /**
152
+ * Set all columns.
153
+ *
154
+ * @param array $columns - Columns values.
155
+ * @return self
156
+ */
157
+ public function setColumns( $columns ) {
158
+ $this->columns = $columns;
159
+ return $this;
160
+ }
161
+
162
+ /**
163
+ * Add conditions.
164
+ *
165
+ * @param string $field - Condition field.
166
+ * @param mixed $value - Condition value.
167
+ * @return self
168
+ */
169
+ public function addCondition( $field, $value ) {
170
+ $this->conditions[ $field ] = $value;
171
+ return $this;
172
+ }
173
+
174
+ /**
175
+ * Add OR condition.
176
+ *
177
+ * @param array $add_conditions - Multi conditions.
178
+ */
179
+ public function addORCondition( $add_conditions ) {
180
+ $this->conditions[] = $add_conditions;
181
+ }
182
+
183
+ /**
184
+ * Clear all conditions.
185
+ *
186
+ * @return self
187
+ */
188
+ public function clearConditions() {
189
+ $this->conditions = array();
190
+ return $this;
191
+ }
192
+
193
+ /**
194
+ * Get all conditions.
195
+ *
196
+ * @return array $conditions
197
+ */
198
+ public function getConditions() {
199
+ return $this->conditions;
200
+ }
201
+
202
+ /**
203
+ * Add order by.
204
+ *
205
+ * @param string $field - Field name.
206
+ * @param boolean $is_descending - (Optional) Ascending/descending.
207
+ * @return self
208
+ */
209
+ public function addOrderBy( $field, $is_descending = false ) {
210
+ $order = ($is_descending) ? 'DESC' : 'ASC';
211
+ $this->orderBy[ $field ] = $order;
212
+ return $this;
213
+ }
214
+
215
+ /**
216
+ * Clear order by.
217
+ *
218
+ * @return self
219
+ */
220
+ public function clearOrderBy() {
221
+ $this->orderBy = array();
222
+ return $this;
223
+ }
224
+
225
+ /**
226
+ * Get order by.
227
+ *
228
+ * @return array $orderBy
229
+ */
230
+ public function getOrderBy() {
231
+ return $this->orderBy;
232
+ }
233
+
234
+ /**
235
+ * Add from.
236
+ *
237
+ * @param string $from_data_set - Data set.
238
+ * @return self
239
+ */
240
+ public function addFrom( $from_data_set ) {
241
+ $this->from[] = $from_data_set;
242
+ return $this;
243
+ }
244
+
245
+ /**
246
+ * Reset from.
247
+ *
248
+ * @return self
249
+ */
250
+ public function clearFrom() {
251
+ $this->from = array();
252
+ return $this;
253
+ }
254
+
255
+ /**
256
+ * Get from.
257
+ *
258
+ * @return string $from data set
259
+ */
260
+ public function getFrom() {
261
+ return $this->from;
262
+ }
263
+
264
+ /**
265
+ * Gets the value of limit.
266
+ *
267
+ * @return mixed
268
+ */
269
+ public function getLimit() {
270
+ return $this->limit;
271
+ }
272
+
273
+ /**
274
+ * Sets the value of limit.
275
+ *
276
+ * @param mixed $limit - The limit.
277
+ * @return self
278
+ */
279
+ public function setLimit( $limit ) {
280
+ $this->limit = $limit;
281
+ return $this;
282
+ }
283
+
284
+ /**
285
+ * Gets the value of offset.
286
+ *
287
+ * @return mixed
288
+ */
289
+ public function getOffset() {
290
+ return $this->offset;
291
+ }
292
+
293
+ /**
294
+ * Sets the value of offset.
295
+ *
296
+ * @param mixed $offset - The offset.
297
+ * @return self
298
+ */
299
+ public function setOffset( $offset ) {
300
+ $this->offset = $offset;
301
+ return $this;
302
+ }
303
+
304
+ /**
305
+ * Adds condition.
306
+ *
307
+ * @param mixed $value - Condition.
308
+ * @return self
309
+ */
310
+ public function addSearchCondition( $value ) {
311
+ $this->searchCondition = $value;
312
+ return $this;
313
+ }
314
+
315
+ /**
316
+ * Gets condition.
317
+ *
318
+ * @return self
319
+ */
320
+ public function getSearchCondition() {
321
+ return $this->searchCondition;
322
+ }
323
+
324
+ /**
325
+ * Check meta join.
326
+ *
327
+ * @return boolean
328
+ */
329
+ public function hasMetaJoin() {
330
+ return $this->meta_join;
331
+ }
332
+
333
+ /**
334
+ * Adds meta join.
335
+ *
336
+ * @return self
337
+ */
338
+ public function addMetaJoin() {
339
+ $this->meta_join = true;
340
+ return $this;
341
+ }
342
  }
classes/Models/TmpUser.php CHANGED
@@ -1,13 +1,44 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
4
  * Model tmp_users
5
  *
6
  * Model used for the Temporary WP_users table.
 
 
7
  */
8
- class WSAL_Models_TmpUser extends WSAL_Models_ActiveRecord
9
- {
10
- public $id = 0;
11
- public $user_login = '';
12
- protected $adapterName = "TmpUser";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  }
1
  <?php
2
  /**
3
+ * Class: TmpUser Model Class
4
+ *
5
+ * Model used for the Temporary WP_users table.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * Model tmp_users
17
  *
18
  * Model used for the Temporary WP_users table.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Models_TmpUser extends WSAL_Models_ActiveRecord {
23
+
24
+ /**
25
+ * User ID.
26
+ *
27
+ * @var integer
28
+ */
29
+ public $id = 0;
30
+
31
+ /**
32
+ * Username.
33
+ *
34
+ * @var string
35
+ */
36
+ public $user_login = '';
37
+
38
+ /**
39
+ * Model Name.
40
+ *
41
+ * @var string
42
+ */
43
+ protected $adapterName = 'TmpUser';
44
  }
classes/Nicer.php CHANGED
@@ -1,299 +1,311 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  * Inspects and prints out PHP values as HTML in a nicer way than print_r().
 
5
  * @author Christian Sciberras <christian@sciberras.me>
6
  * @copyright (c) 2013, Christian Sciberras
7
  * @license https://raw.github.com/uuf6429/nice_r/master/LICENSE MIT License
8
  * @link https://github.com/uuf6429/nice_r GitHub Repository
9
  * @version 2.0
10
  * @since 2.0
 
11
  */
12
- class WSAL_Nicer
13
- {
14
- protected $value;
15
-
16
- /**
17
- * Allows modification of CSS class prefix.
18
- * @var string
19
- */
20
- public $css_class = 'nice_r';
21
-
22
- /**
23
- * Allows modification of HTML id prefix.
24
- * @var string
25
- */
26
- public $html_id = 'nice_r_v';
27
-
28
- /**
29
- * Allows modification of JS function used to toggle sections.
30
- * @var string
31
- */
32
- public $js_func = 'nice_r_toggle';
33
-
34
- /**
35
- * Whether to inspect and output methods for objects or not.
36
- * @var boolean
37
- */
38
- public $inspect_methods = false;
39
-
40
- /**
41
- * Since PHP does not support private constants, we'll have to settle for private static fields.
42
- * @var string
43
- */
44
- protected static $BEEN_THERE = '__NICE_R_INFINITE_RECURSION_PROTECT__';
45
-
46
- protected $_has_reflection = null;
47
-
48
- /**
49
- * Constructs new renderer instance.
50
- * @param mixed $value The value to inspect and render.
51
- * @param boolean $inspectMethods Whether to inspect and output methods for objects or not.
52
- */
53
- public function __construct($value, $inspectMethods = false)
54
- {
55
- $this->value = $value;
56
- $this->inspect_methods = $inspectMethods;
57
-
58
- if (is_null($this->_has_reflection)) {
59
- $this->_has_reflection = class_exists('ReflectionClass');
60
- }
61
- }
62
-
63
- /**
64
- * Generates the inspector HTML and returns it as a string.
65
- * @return string Generated HTML.
66
- */
67
- public function generate()
68
- {
69
- return $this->_generate_value($this->value, $this->css_class);
70
- }
71
-
72
- /**
73
- * Renders the inspector HTML directly to the browser.
74
- */
75
- public function render()
76
- {
77
- echo $this->generate();
78
- }
79
-
80
- /**
81
- * Converts a string to HTML, encoding any special characters.
82
- * @param string $text The original string.
83
- * @return string The string as HTML.
84
- */
85
- protected function _esc_html($text)
86
- {
87
- return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
88
- }
89
-
90
- protected function _inspect_array(&$html, &$var)
91
- {
92
- $has_subitems = false;
93
-
94
- foreach ($var as $k => $v) {
95
- if ($k !== self::$BEEN_THERE) {
96
- $html .= $this->_generate_keyvalue($k, $v);
97
- $has_subitems = true;
98
- }
99
- }
100
-
101
- if (!$has_subitems) {
102
- $html .= '<span class="'.$this->css_class.'_ni">Empty Array</span>';
103
- }
104
- }
105
-
106
- protected function _inspect_object(&$html, &$var)
107
- {
108
- // render properties
109
- $has_subitems = false;
110
-
111
- foreach ((array)$var as $k => $v) {
112
- if ($k !== self::$BEEN_THERE) {
113
- $html .= $this->_generate_keyvalue($k, $v);
114
- $has_subitems = true;
115
- }
116
- }
117
-
118
- if (!$has_subitems) {
119
- $html .= '<span class="'.$this->css_class.'_ni">No Properties</span>';
120
- }
121
-
122
- // render methods (if enabled)
123
- if ($this->inspect_methods) {
124
- $has_subitems = false;
125
-
126
- foreach ((array)get_class_methods($var) as $method) {
127
- $html .= $this->_generate_callable($var, $method);
128
- $has_subitems = true;
129
- }
130
-
131
- if (!$has_subitems) {
132
- $html .= '<span class="'.$this->css_class.'_ni">No Methods</span>';
133
- }
134
- }
135
- }
136
-
137
- /**
138
- * Render a single particular value.
139
- * @param mixed $var The value to render
140
- * @param string $class Parent CSS class.
141
- * @param string $id Item HTML id.
142
- */
143
- protected function _generate_value($var, $class = '', $id = '')
144
- {
145
- $BEENTHERE = self::$BEEN_THERE;
146
- $class .= ' '.$this->css_class.'_t_'.gettype($var);
147
-
148
- $html = '<div id="'.$id.'" class="'.$class.'">';
149
- switch (true) {
150
- // handle arrays
151
- case is_array($var):
152
- if (isset($var[$BEENTHERE])) {
153
- $html .= '<span class="'.$this->css_class.'_ir">Infinite Recursion Detected!</span>';
154
- } else {
155
- $var[$BEENTHERE] = true;
156
- $this->_inspect_array($html, $var);
157
- unset($var[$BEENTHERE]);
158
- }
159
- break;
160
-
161
- // handle objects
162
- case is_object($var):
163
- if (isset($var->$BEENTHERE)) {
164
- $html .= '<span class="'.$this->css_class.'_ir">Infinite Recursion Detected!</span>';
165
- } else {
166
- $var->$BEENTHERE = true;
167
- $this->_inspect_object($html, $var);
168
- unset($var->$BEENTHERE);
169
- }
170
- break;
171
-
172
- // handle simple types
173
- default:
174
- $html .= $this->_generate_keyvalue('', $var);
175
- break;
176
- }
177
-
178
- return $html . '</div>';
179
- }
180
-
181
- /**
182
- * Generates a new unique ID for tagging elements.
183
- * @staticvar int $id
184
- * @return integer An ID unique per request.
185
- */
186
- protected function _generate_dropid()
187
- {
188
- static $id = 0;
189
- return ++$id;
190
- }
191
-
192
- /**
193
- * Render a key-value pair.
194
- * @staticvar int $id Specifies element id.
195
- * @param string $key Key name.
196
- * @param mixed $val Key value.
197
- */
198
- protected function _generate_keyvalue($key, $val)
199
- {
200
- $id = $this->_generate_dropid();
201
- $p = ''; // preview
202
- $d = ''; // description
203
- $t = gettype($val); // get data type
204
- $is_hash = ($t=='array') || ($t=='object');
205
-
206
- switch ($t) {
207
- case 'boolean':
208
- $p = $val ? 'TRUE' : 'FALSE';
209
- break;
210
- case 'integer':
211
- case 'double':
212
- $p = (string)$val;
213
- break;
214
- case 'string':
215
- $d .= ', '.strlen($val).' characters';
216
- $p = $val;
217
- break;
218
- case 'resource':
219
- $d .= ', '.get_resource_type($val).' type';
220
- $p = (string)$val;
221
- break;
222
- case 'array':
223
- $d .= ', '.count($val).' elements';
224
- break;
225
- case 'object':
226
- $d .= ', '.get_class($val).', '.count(get_object_vars($val)).' properties';
227
- break;
228
- }
229
-
230
- $cls = $this->css_class;
231
- $xcls = !$is_hash ? $cls.'_ad' : '';
232
- $html = '<a '.($is_hash?'href="javascript:;"':'').' onclick="'.$this->js_func.'(\''.$this->html_id.'\',\''.$id.'\');">';
233
- $html .= ' <span class="'.$cls.'_a '.$xcls.'" id="'.$this->html_id.'_a'.$id.'">&#9658;</span>';
234
- $html .= ' <span class="'.$cls.'_k">'.$this->_esc_html($key).'</span>';
235
- $html .= ' <span class="'.$cls.'_d">(<span>'.ucwords($t).'</span>'.$d.')</span>';
236
- $html .= ' <span class="'.$cls.'_p '.$cls.'_t_'.$t.'">'.$this->_esc_html($p).'</span>';
237
- $html .= '</a>';
238
-
239
- if ($is_hash) {
240
- $html .= $this->_generate_value($val, $cls.'_v', $this->html_id.'_v'.$id);
241
- }
242
-
243
- return $html;
244
- }
245
-
246
- protected function _generate_callable($context, $callback)
247
- {
248
- $id = $this->_generate_dropid();
249
- $ref = null;
250
- $name = 'Anonymous';
251
- $cls = $this->css_class;
252
-
253
- if ($this->_has_reflection) {
254
- if (is_null($context)) {
255
- $ref = new ReflectionFunction($callback);
256
- } else {
257
- $ref = new ReflectionMethod($context, $callback);
258
- }
259
- $name = $ref->getName();
260
- } elseif (is_string($callback)) {
261
- $name = $callback;
262
- }
263
-
264
- if (!is_null($ref)) {
265
- $doc = $ref->getDocComment();
266
- $prms = array();
267
- foreach ($ref->getParameters() as $p) {
268
- $prms[] = '$' . $p->getName() . (
269
- $p->isDefaultValueAvailable()
270
- ? (
271
- ' = <span class="'.$cls.'_mv">' . (
272
- $p->isDefaultValueConstant()
273
- ? $p->getDefaultValueConstantName()
274
- : var_export($p->getDefaultValue(), true)
275
- ) . '</span>'
276
- )
277
- : ''
278
- );
279
- }
280
- } else {
281
- $doc = null;
282
- $prms = array('???');
283
- }
284
-
285
- $xcls = !$doc ? $cls.'_ad' : '';
286
- $html = '<a class="'.$cls.'_c" '.($doc?'href="javascript:;"':'').' onclick="'.$this->js_func.'(\''.$this->html_id.'\',\''.$id.'\');">';
287
- $html .= ' <span class="'.$cls.'_a '.$xcls.'">&#9658;</span>';
288
- $html .= ' <span class="'.$cls.'_k">'.$this->_esc_html($name).'<span class="'.$cls.'_ma">(<span>'.implode(', ', $prms).'</span>)</span></span>';
289
- $html .= '</a>';
290
-
291
- if ($doc) {
292
- $html .= '<div id="'.$this->html_id.'_v'.$id.'" class="nice_r_v '.$this->css_class.'_t_comment">';
293
- $html .= nl2br(str_replace(' ', '&nbsp;', $this->_esc_html($doc)));
294
- $html .= '</div>';
295
- }
296
-
297
- return $html;
298
- }
 
 
 
 
 
 
 
 
 
 
 
299
  }
1
  <?php
2
  /**
 
3
  * Inspects and prints out PHP values as HTML in a nicer way than print_r().
4
+ *
5
  * @author Christian Sciberras <christian@sciberras.me>
6
  * @copyright (c) 2013, Christian Sciberras
7
  * @license https://raw.github.com/uuf6429/nice_r/master/LICENSE MIT License
8
  * @link https://github.com/uuf6429/nice_r GitHub Repository
9
  * @version 2.0
10
  * @since 2.0
11
+ * @package Wsal
12
  */
13
+ class WSAL_Nicer {
14
+
15
+ /**
16
+ * Input string.
17
+ *
18
+ * @var mix
19
+ */
20
+ protected $value;
21
+
22
+ /**
23
+ * Allows modification of CSS class prefix.
24
+ *
25
+ * @var string
26
+ */
27
+ public $css_class = 'nice_r';
28
+
29
+ /**
30
+ * Allows modification of HTML id prefix.
31
+ *
32
+ * @var string
33
+ */
34
+ public $html_id = 'nice_r_v';
35
+
36
+ /**
37
+ * Allows modification of JS function used to toggle sections.
38
+ *
39
+ * @var string
40
+ */
41
+ public $js_func = 'nice_r_toggle';
42
+
43
+ /**
44
+ * Whether to inspect and output methods for objects or not.
45
+ *
46
+ * @var boolean
47
+ */
48
+ public $inspect_methods = false;
49
+
50
+ /**
51
+ * Since PHP does not support private constants, we'll have to settle for private static fields.
52
+ *
53
+ * @var string
54
+ */
55
+ protected static $BEEN_THERE = '__NICE_R_INFINITE_RECURSION_PROTECT__';
56
+
57
+ /**
58
+ * True if ReflectionClass exists.
59
+ *
60
+ * @var bool
61
+ */
62
+ protected $_has_reflection = null;
63
+
64
+ /**
65
+ * Constructs new renderer instance.
66
+ *
67
+ * @param mixed $value The value to inspect and render.
68
+ * @param boolean $inspect_methods Whether to inspect and output methods for objects or not.
69
+ */
70
+ public function __construct( $value, $inspect_methods = false ) {
71
+ $this->value = $value;
72
+ $this->inspect_methods = $inspect_methods;
73
+
74
+ if ( is_null( $this->_has_reflection ) ) {
75
+ $this->_has_reflection = class_exists( 'ReflectionClass' );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Generates the inspector HTML and returns it as a string.
81
+ *
82
+ * @return string Generated HTML.
83
+ */
84
+ public function generate() {
85
+ return $this->_generate_value( $this->value, $this->css_class );
86
+ }
87
+
88
+ /**
89
+ * Renders the inspector HTML directly to the browser.
90
+ */
91
+ public function render() {
92
+ echo $this->generate();
93
+ }
94
+
95
+ /**
96
+ * Converts a string to HTML, encoding any special characters.
97
+ *
98
+ * @param string $text The original string.
99
+ * @return string The string as HTML.
100
+ */
101
+ protected function _esc_html( $text ) {
102
+ return htmlspecialchars( $text, ENT_QUOTES, 'UTF-8' );
103
+ }
104
+
105
+ protected function _inspect_array( &$html, &$var ) {
106
+ $has_subitems = false;
107
+
108
+ foreach ( $var as $k => $v ) {
109
+ if ( $k !== self::$BEEN_THERE ) {
110
+ $html .= $this->_generate_keyvalue( $k, $v );
111
+ $has_subitems = true;
112
+ }
113
+ }
114
+
115
+ if ( ! $has_subitems ) {
116
+ $html .= '<span class="' . $this->css_class . '_ni">Empty Array</span>';
117
+ }
118
+ }
119
+
120
+ protected function _inspect_object( &$html, &$var ) {
121
+ // Render properties.
122
+ $has_subitems = false;
123
+
124
+ foreach ( (array) $var as $k => $v ) {
125
+ if ( $k !== self::$BEEN_THERE ) {
126
+ $html .= $this->_generate_keyvalue( $k, $v );
127
+ $has_subitems = true;
128
+ }
129
+ }
130
+
131
+ if ( ! $has_subitems ) {
132
+ $html .= '<span class="' . $this->css_class . '_ni">No Properties</span>';
133
+ }
134
+
135
+ // Render methods (if enabled).
136
+ if ( $this->inspect_methods ) {
137
+ $has_subitems = false;
138
+
139
+ foreach ( (array) get_class_methods( $var ) as $method ) {
140
+ $html .= $this->_generate_callable( $var, $method );
141
+ $has_subitems = true;
142
+ }
143
+
144
+ if ( ! $has_subitems ) {
145
+ $html .= '<span class="' . $this->css_class . '_ni">No Methods</span>';
146
+ }
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Render a single particular value.
152
+ *
153
+ * @param mixed $var The value to render.
154
+ * @param string $class Parent CSS class.
155
+ * @param string $id Item HTML id.
156
+ */
157
+ protected function _generate_value( $var, $class = '', $id = '' ) {
158
+ $BEENTHERE = self::$BEEN_THERE;
159
+ $class .= ' ' . $this->css_class . '_t_' . gettype( $var );
160
+
161
+ $html = '<div id="' . $id . '" class="' . $class . '">';
162
+ switch ( true ) {
163
+ // Handle arrays.
164
+ case is_array( $var ):
165
+ if ( isset( $var[ $BEENTHERE ] ) ) {
166
+ $html .= '<span class="' . $this->css_class . '_ir">Infinite Recursion Detected!</span>';
167
+ } else {
168
+ $var[ $BEENTHERE ] = true;
169
+ $this->_inspect_array( $html, $var );
170
+ unset( $var[ $BEENTHERE ] );
171
+ }
172
+ break;
173
+
174
+ // Handle objects.
175
+ case is_object( $var ):
176
+ if ( isset( $var->$BEENTHERE ) ) {
177
+ $html .= '<span class="' . $this->css_class . '_ir">Infinite Recursion Detected!</span>';
178
+ } else {
179
+ $var->$BEENTHERE = true;
180
+ $this->_inspect_object( $html, $var );
181
+ unset( $var->$BEENTHERE );
182
+ }
183
+ break;
184
+
185
+ // Handle simple types.
186
+ default:
187
+ $html .= $this->_generate_keyvalue( '', $var );
188
+ break;
189
+ }
190
+
191
+ return $html . '</div>';
192
+ }
193
+
194
+ /**
195
+ * Generates a new unique ID for tagging elements.
196
+ *
197
+ * @staticvar int $id
198
+ * @return integer An ID unique per request.
199
+ */
200
+ protected function _generate_dropid() {
201
+ static $id = 0;
202
+ return ++$id;
203
+ }
204
+
205
+ /**
206
+ * Render a key-value pair.
207
+ *
208
+ * @staticvar int $id Specifies element id.
209
+ * @param string $key Key name.
210
+ * @param mixed $val Key value.
211
+ */
212
+ protected function _generate_keyvalue( $key, $val ) {
213
+ $id = $this->_generate_dropid();
214
+ $p = ''; // Preview
215
+ $d = ''; // Description
216
+ $t = gettype( $val ); // Get data type.
217
+ $is_hash = ( 'array' == $t ) || ( 'object' == $t );
218
+
219
+ switch ( $t ) {
220
+ case 'boolean':
221
+ $p = $val ? 'TRUE' : 'FALSE';
222
+ break;
223
+ case 'integer':
224
+ case 'double':
225
+ $p = (string) $val;
226
+ break;
227
+ case 'string':
228
+ $d .= ', ' . strlen( $val ) . ' characters';
229
+ $p = $val;
230
+ break;
231
+ case 'resource':
232
+ $d .= ', ' . get_resource_type( $val ) . ' type';
233
+ $p = (string) $val;
234
+ break;
235
+ case 'array':
236
+ $d .= ', ' . count( $val ) . ' elements';
237
+ break;
238
+ case 'object':
239
+ $d .= ', ' . get_class( $val ) . ', ' . count( get_object_vars( $val ) ) . ' properties';
240
+ break;
241
+ }
242
+
243
+ $cls = $this->css_class;
244
+ $xcls = ! $is_hash ? $cls . '_ad' : '';
245
+ $html = '<a ' . ($is_hash ? 'href="javascript:;"' : '') . ' onclick="' . $this->js_func . '(\'' . $this->html_id . '\',\'' . $id . '\');">';
246
+ $html .= ' <span class="' . $cls . '_a ' . $xcls . '" id="' . $this->html_id . '_a' . $id . '">&#9658;</span>';
247
+ $html .= ' <span class="' . $cls . '_k">' . $this->_esc_html( $key ) . '</span>';
248
+ $html .= ' <span class="' . $cls . '_d">(<span>' . ucwords( $t ) . '</span>' . $d . ')</span>';
249
+ $html .= ' <span class="' . $cls . '_p ' . $cls . '_t_' . $t . '">' . $this->_esc_html( $p ) . '</span>';
250
+ $html .= '</a>';
251
+
252
+ if ( $is_hash ) {
253
+ $html .= $this->_generate_value( $val, $cls . '_v', $this->html_id . '_v' . $id );
254
+ }
255
+
256
+ return $html;
257
+ }
258
+
259
+ protected function _generate_callable( $context, $callback ) {
260
+ $id = $this->_generate_dropid();
261
+ $ref = null;
262
+ $name = 'Anonymous';
263
+ $cls = $this->css_class;
264
+
265
+ if ( $this->_has_reflection ) {
266
+ if ( is_null( $context ) ) {
267
+ $ref = new ReflectionFunction( $callback );
268
+ } else {
269
+ $ref = new ReflectionMethod( $context, $callback );
270
+ }
271
+ $name = $ref->getName();
272
+ } elseif ( is_string( $callback ) ) {
273
+ $name = $callback;
274
+ }
275
+
276
+ if ( ! is_null( $ref ) ) {
277
+ $doc = $ref->getDocComment();
278
+ $prms = array();
279
+ foreach ( $ref->getParameters() as $p ) {
280
+ $prms[] = '$' . $p->getName() . (
281
+ $p->isDefaultValueAvailable()
282
+ ? (
283
+ ' = <span class="' . $cls . '_mv">' . (
284
+ $p->isDefaultValueConstant()
285
+ ? $p->getDefaultValueConstantName()
286
+ : var_export( $p->getDefaultValue(), true )
287
+ ) . '</span>'
288
+ )
289
+ : ''
290
+ );
291
+ }
292
+ } else {
293
+ $doc = null;
294
+ $prms = array( '???' );
295
+ }
296
+
297
+ $xcls = ! $doc ? $cls . '_ad' : '';
298
+ $html = '<a class="' . $cls . '_c" ' . ($doc ? 'href="javascript:;"' : '') . ' onclick="' . $this->js_func . '(\'' . $this->html_id . '\',\'' . $id . '\');">';
299
+ $html .= ' <span class="' . $cls . '_a ' . $xcls . '">&#9658;</span>';
300
+ $html .= ' <span class="' . $cls . '_k">' . $this->_esc_html( $name ) . '<span class="' . $cls . '_ma">(<span>' . implode( ', ', $prms ) . '</span>)</span></span>';
301
+ $html .= '</a>';
302
+
303
+ if ( $doc ) {
304
+ $html .= '<div id="' . $this->html_id . '_v' . $id . '" class="nice_r_v ' . $this->css_class . '_t_comment">';
305
+ $html .= nl2br( str_replace( ' ', '&nbsp;', $this->_esc_html( $doc ) ) );
306
+ $html .= '</div>';
307
+ }
308
+
309
+ return $html;
310
+ }
311
  }
classes/SensorManager.php CHANGED
@@ -1,75 +1,88 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * This Class load all the sensors and initialize them.
 
 
6
  */
7
- final class WSAL_SensorManager extends WSAL_AbstractSensor
8
- {
9
- /**
10
- * @var WSAL_AbstractSensor[]
11
- */
12
- protected $sensors = array();
13
-
14
- public function __construct(WpSecurityAuditLog $plugin)
15
- {
16
- parent::__construct($plugin);
17
-
18
- foreach (glob(dirname(__FILE__) . '/Sensors/*.php') as $file) {
19
- $this->AddFromFile($file);
20
- }
21
- /**
22
- * Load Custom Sensor files from /wp-content/uploads/wp-security-audit-log/custom-sensors/
23
- */
24
- $upload_dir = wp_upload_dir();
25
- $uploadsDirPath = trailingslashit($upload_dir['basedir']) . 'wp-security-audit-log' . DIRECTORY_SEPARATOR . 'custom-sensors' . DIRECTORY_SEPARATOR;
26
- // Check directory
27
- if (is_dir($uploadsDirPath) && is_readable($uploadsDirPath)) {
28
- foreach (glob($uploadsDirPath . '*.php') as $file) {
29
- require_once($file);
30
- $file = substr($file, 0, -4);
31
- $class = "WSAL_Sensors_" . str_replace($uploadsDirPath, '', $file);
32
- $this->AddFromClass($class);
33
- }
34
- }
35
- }
36
-
37
- public function HookEvents()
38
- {
39
- foreach ($this->sensors as $sensor) {
40
- $sensor->HookEvents();
41
- }
42
- }
43
-
44
- public function GetSensors()
45
- {
46
- return $this->sensors;
47
- }
48
-
49
- /**
50
- * Add new sensor from file inside autoloader path.
51
- * @param string $file Path to file.
52
- */
53
- public function AddFromFile($file)
54
- {
55
- $this->AddFromClass($this->plugin->GetClassFileClassName($file));
56
- }
57
-
58
- /**
59
- * Add new sensor given class name.
60
- * @param string $class Class name.
61
- */
62
- public function AddFromClass($class)
63
- {
64
- $this->AddInstance(new $class($this->plugin));
65
- }
66
-
67
- /**
68
- * Add newly created sensor to list.
69
- * @param WSAL_AbstractSensor $sensor The new sensor.
70
- */
71
- public function AddInstance(WSAL_AbstractSensor $sensor)
72
- {
73
- $this->sensors[] = $sensor;
74
- }
 
 
 
 
 
 
 
 
 
 
 
75
  }
1
  <?php
2
  /**
3
+ * Sensor Manager.
4
  *
5
  * This Class load all the sensors and initialize them.
6
+ *
7
+ * @package Wsal
8
  */
9
+ final class WSAL_SensorManager extends WSAL_AbstractSensor {
10
+
11
+ /**
12
+ * Array of sensors.
13
+ *
14
+ * @var WSAL_AbstractSensor[]
15
+ */
16
+ protected $sensors = array();
17
+
18
+ /**
19
+ * Method: Constructor.
20
+ *
21
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
22
+ */
23
+ public function __construct( WpSecurityAuditLog $plugin ) {
24
+ parent::__construct( $plugin );
25
+
26
+ foreach ( glob( dirname( __FILE__ ) . '/Sensors/*.php' ) as $file ) {
27
+ $this->AddFromFile( $file );
28
+ }
29
+
30
+ /**
31
+ * Load Custom Sensor files from /wp-content/uploads/wp-security-audit-log/custom-sensors/
32
+ */
33
+ $upload_dir = wp_upload_dir();
34
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log' . DIRECTORY_SEPARATOR . 'custom-sensors' . DIRECTORY_SEPARATOR;
35
+ // Check directory.
36
+ if ( is_dir( $uploads_dir_path ) && is_readable( $uploads_dir_path ) ) {
37
+ foreach ( glob( $uploads_dir_path . '*.php' ) as $file ) {
38
+ require_once( $file );
39
+ $file = substr( $file, 0, -4 );
40
+ $class = 'WSAL_Sensors_' . str_replace( $uploads_dir_path, '', $file );
41
+ $this->AddFromClass( $class );
42
+ }
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Method: Hook events of the sensors.
48
+ */
49
+ public function HookEvents() {
50
+ foreach ( $this->sensors as $sensor ) {
51
+ $sensor->HookEvents();
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Method: Get the sensors.
57
+ */
58
+ public function GetSensors() {
59
+ return $this->sensors;
60
+ }
61
+
62
+ /**
63
+ * Add new sensor from file inside autoloader path.
64
+ *
65
+ * @param string $file Path to file.
66
+ */
67
+ public function AddFromFile( $file ) {
68
+ $this->AddFromClass( $this->plugin->GetClassFileClassName( $file ) );
69
+ }
70
+
71
+ /**
72
+ * Add new sensor given class name.
73
+ *
74
+ * @param string $class Class name.
75
+ */
76
+ public function AddFromClass( $class ) {
77
+ $this->AddInstance( new $class( $this->plugin ) );
78
+ }
79
+
80
+ /**
81
+ * Add newly created sensor to list.
82
+ *
83
+ * @param WSAL_AbstractSensor $sensor The new sensor.
84
+ */
85
+ public function AddInstance( WSAL_AbstractSensor $sensor ) {
86
+ $this->sensors[] = $sensor;
87
+ }
88
  }
classes/Sensors/BBPress.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Support for BBPress Forum Plugin.
6
  *
7
  * 8000 User created new forum
@@ -23,523 +35,607 @@
23
  * 8020 User permanently deleted topic
24
  * 8021 User restored topic from trash
25
  * 8022 User changed visibility of a topic
 
 
 
26
  */
27
- class WSAL_Sensors_BBPress extends WSAL_AbstractSensor
28
- {
29
- /**
30
- * @var string old permalink
31
- */
32
- protected $_OldLink = null;
33
-
34
- /**
35
- * Listening to events using WP hooks.
36
- */
37
- public function HookEvents()
38
- {
39
- if (current_user_can("edit_posts")) {
40
- add_action('admin_init', array($this, 'EventAdminInit'));
41
- }
42
- add_action('post_updated', array($this, 'CheckForumChange'), 10, 3);
43
- add_action('delete_post', array($this, 'EventForumDeleted'), 10, 1);
44
- add_action('wp_trash_post', array($this, 'EventForumTrashed'), 10, 1);
45
- add_action('untrash_post', array($this, 'EventForumUntrashed'));
46
- }
47
-
48
- /**
49
- * Triggered when a user accesses the admin area.
50
- */
51
- public function EventAdminInit()
52
- {
53
- // load old data, if applicable
54
- $this->RetrieveOldData();
55
- // check for Ajax changes
56
- $this->TriggerAjaxChange();
57
- }
58
-
59
- /**
60
- * Retrieve Old data.
61
- * @global mixed $_POST post data
62
- */
63
- protected function RetrieveOldData()
64
- {
65
- if (isset($_POST) && isset($_POST['post_ID'])
66
- && !(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
67
- && !(isset($_POST['action']) && $_POST['action'] == 'autosave')
68
- ) {
69
- $postID = intval($_POST['post_ID']);
70
- $this->_OldLink = get_permalink($postID);
71
- }
72
- }
73
-
74
- /**
75
- * Calls event forum changes.
76
- * @param integer $post_ID post ID
77
- * @param stdClass $newpost the new post
78
- * @param stdClass $oldpost the old post
79
- */
80
- public function CheckForumChange($post_ID, $newpost, $oldpost)
81
- {
82
- if ($this->CheckBBPress($oldpost)) {
83
- $changes = 0 + $this->EventForumCreation($oldpost, $newpost);
84
- // Change Visibility
85
- if (!$changes) {
86
- $changes = $this->EventForumChangedVisibility($oldpost);
87
- }
88
- // Change Type
89
- if (!$changes) {
90
- $changes = $this->EventForumChangedType($oldpost);
91
- }
92
- // Change status
93
- if (!$changes) {
94
- $changes = $this->EventForumChangedStatus($oldpost);
95
- }
96
- // Change Order, Parent or URL
97
- if (!$changes) {
98
- $changes = $this->EventForumChanged($oldpost, $newpost);
99
- }
100
- }
101
- }
102
-
103
- /**
104
- * Permanently deleted.
105
- * @param integer $post_id post ID
106
- */
107
- public function EventForumDeleted($post_id)
108
- {
109
- $post = get_post($post_id);
110
- if ($this->CheckBBPress($post)) {
111
- switch ($post->post_type) {
112
- case 'forum':
113
- $this->EventForumByCode($post, 8006);
114
- break;
115
- case 'topic':
116
- $this->EventTopicByCode($post, 8020);
117
- break;
118
- }
119
- }
120
- }
121
-
122
- /**
123
- * Moved to Trash.
124
- * @param integer $post_id post ID
125
- */
126
- public function EventForumTrashed($post_id)
127
- {
128
- $post = get_post($post_id);
129
- if ($this->CheckBBPress($post)) {
130
- switch ($post->post_type) {
131
- case 'forum':
132
- $this->EventForumByCode($post, 8005);
133
- break;
134
- case 'topic':
135
- $this->EventTopicByCode($post, 8019);
136
- break;
137
- }
138
- }
139
- }
140
-
141
- /**
142
- * Restored from Trash.
143
- * @param integer $post_id post ID
144
- */
145
- public function EventForumUntrashed($post_id)
146
- {
147
- $post = get_post($post_id);
148
- if ($this->CheckBBPress($post)) {
149
- switch ($post->post_type) {
150
- case 'forum':
151
- $this->EventForumByCode($post, 8007);
152
- break;
153
- case 'topic':
154
- $this->EventTopicByCode($post, 8021);
155
- break;
156
- }
157
- }
158
- }
159
-
160
- /**
161
- * Check post type.
162
- * @param stdClass $post post
163
- * @return boolean
164
- */
165
- private function CheckBBPress($post)
166
- {
167
- switch ($post->post_type) {
168
- case 'forum':
169
- case 'topic':
170
- case 'reply':
171
- return true;
172
- default:
173
- return false;
174
- }
175
- }
176
-
177
- /**
178
- * Event post creation.
179
- * @param stdClass $old_post the old post
180
- * @param stdClass $new_post the new post
181
- * @return boolean
182
- */
183
- private function EventForumCreation($old_post, $new_post)
184
- {
185
- $original = isset($_POST['original_post_status']) ? $_POST['original_post_status'] : '';
186
- if ($old_post->post_status == 'draft' || $original == 'auto-draft') {
187
- $editorLink = $this->GetEditorLink($new_post);
188
- if ($new_post->post_status == 'publish') {
189
- switch ($old_post->post_type) {
190
- case 'forum':
191
- $this->plugin->alerts->Trigger(8000, array(
192
- 'ForumName' => $new_post->post_title,
193
- 'ForumURL' => get_permalink($new_post->ID),
194
- $editorLink['name'] => $editorLink['value']
195
- ));
196
- break;
197
- case 'topic':
198
- $this->plugin->alerts->Trigger(8014, array(
199
- 'TopicName' => $new_post->post_title,
200
- 'TopicURL' => get_permalink($new_post->ID),
201
- $editorLink['name'] => $editorLink['value']
202
- ));
203
- break;
204
- }
205
- return 1;
206
- }
207
- }
208
- return 0;
209
- }
210
-
211
- /**
212
- * Event post changed visibility.
213
- * @param stdClass $post the post
214
- * @return boolean $result
215
- */
216
- private function EventForumChangedVisibility($post)
217
- {
218
- $result = 0;
219
- $editorLink = $this->GetEditorLink($post);
220
- switch ($post->post_type) {
221
- case 'forum':
222
- $oldVisibility = !empty($_REQUEST['visibility']) ? $_REQUEST['visibility'] : '';
223
- $newVisibility = !empty($_REQUEST['bbp_forum_visibility']) ? $_REQUEST['bbp_forum_visibility'] : '';
224
- $newVisibility = ($newVisibility == 'publish') ? 'public' : $newVisibility;
225
-
226
- if (!empty($newVisibility) && $oldVisibility != 'auto-draft' && $oldVisibility != $newVisibility) {
227
- $this->plugin->alerts->Trigger(8002, array(
228
- 'ForumName' => $post->post_title,
229
- 'OldVisibility' => $oldVisibility,
230
- 'NewVisibility' => $newVisibility,
231
- $editorLink['name'] => $editorLink['value']
232
- ));
233
- $result = 1;
234
- }
235
- break;
236
- case 'topic':
237
- $oldVisibility = !empty($_REQUEST['hidden_post_visibility']) ? $_REQUEST['hidden_post_visibility'] : '';
238
- $newVisibility = !empty($_REQUEST['visibility']) ? $_REQUEST['visibility'] : '';
239
- $newVisibility = ($newVisibility == 'password') ? 'password protected' : $newVisibility;
240
-
241
- if (!empty($newVisibility) && $oldVisibility != 'auto-draft' && $oldVisibility != $newVisibility) {
242
- $this->plugin->alerts->Trigger(8022, array(
243
- 'TopicName' => $post->post_title,
244
- 'OldVisibility' => $oldVisibility,
245
- 'NewVisibility' => $newVisibility,
246
- $editorLink['name'] => $editorLink['value']
247
- ));
248
- $result = 1;
249
- }
250
- break;
251
- }
252
- return $result;
253
- }
254
-
255
- /**
256
- * Event post changed type.
257
- * @param stdClass $post the post
258
- * @return boolean $result
259
- */
260
- private function EventForumChangedType($post)
261
- {
262
- $result = 0;
263
- $editorLink = $this->GetEditorLink($post);
264
- switch ($post->post_type) {
265
- case 'forum':
266
- $bbp_forum_type = get_post_meta($post->ID, '_bbp_forum_type', true);
267
- $oldType = !empty($bbp_forum_type) ? $bbp_forum_type : 'forum';
268
- $newType = !empty($_POST['bbp_forum_type']) ? $_POST['bbp_forum_type'] : '';
269
- if (!empty($newType) && $oldType != $newType) {
270
- $this->plugin->alerts->Trigger(8011, array(
271
- 'ForumName' => $post->post_title,
272
- 'OldType' => $oldType,
273
- 'NewType' => $newType,
274
- $editorLink['name'] => $editorLink['value']
275
- ));
276
- $result = 1;
277
- }
278
- break;
279
- case 'topic':
280
- if (!empty($_POST['parent_id'])) {
281
- $post_id = $_POST['parent_id'];
282
- } else {
283
- $post_id = $post->ID;
284
- }
285
- $bbp_sticky_topics = maybe_unserialize(get_post_meta($post_id, '_bbp_sticky_topics', true));
286
- $fn = $this->IsMultisite() ? 'get_site_option' : 'get_option';
287
- $bbp_super_sticky_topics = maybe_unserialize($fn('_bbp_super_sticky_topics'));
288
- if (!empty($bbp_sticky_topics) && in_array($post->ID, $bbp_sticky_topics)) {
289
- $oldType = 'sticky';
290
- } elseif (!empty($bbp_super_sticky_topics) && in_array($post->ID, $bbp_super_sticky_topics)) {
291
- $oldType = 'super';
292
- } else {
293
- $oldType = 'unstick';
294
- }
295
- $newType = !empty($_POST['bbp_stick_topic']) ? $_POST['bbp_stick_topic'] : '';
296
- if (!empty($newType) && $oldType != $newType) {
297
- $this->plugin->alerts->Trigger(8016, array(
298
- 'TopicName' => $post->post_title,
299
- 'OldType' => ($oldType == 'unstick') ? 'normal' : (($oldType == 'super') ? 'super sticky' : $oldType),
300
- 'NewType' => ($newType == 'unstick') ? 'normal' : (($newType == 'super') ? 'super sticky' : $newType),
301
- $editorLink['name'] => $editorLink['value']
302
- ));
303
- $result = 1;
304
- }
305
- break;
306
- }
307
- return $result;
308
- }
309
-
310
- /**
311
- * Event post changed status.
312
- * @param stdClass $post the post
313
- * @return boolean $result
314
- */
315
- private function EventForumChangedStatus($post)
316
- {
317
- $result = 0;
318
- $editorLink = $this->GetEditorLink($post);
319
- switch ($post->post_type) {
320
- case 'forum':
321
- $bbp_status = get_post_meta($post->ID, '_bbp_status', true);
322
- $oldStatus = !empty($bbp_status) ? $bbp_status : 'open';
323
- $newStatus = !empty($_REQUEST['bbp_forum_status']) ? $_REQUEST['bbp_forum_status'] : '';
324
- if (!empty($newStatus) && $oldStatus != $newStatus) {
325
- $this->plugin->alerts->Trigger(8001, array(
326
- 'ForumName' => $post->post_title,
327
- 'OldStatus' => $oldStatus,
328
- 'NewStatus' => $newStatus,
329
- $editorLink['name'] => $editorLink['value']
330
- ));
331
- $result = 1;
332
- }
333
- break;
334
- case 'topic':
335
- $oldStatus = !empty($_REQUEST['original_post_status']) ? $_REQUEST['original_post_status'] : '';
336
- $newStatus = !empty($_REQUEST['post_status']) ? $_REQUEST['post_status'] : '';
337
- // In case of Ajax request Spam/Not spam
338
- if (isset($_GET['action']) && $_GET['action'] == 'bbp_toggle_topic_spam') {
339
- $oldStatus = $post->post_status;
340
- $newStatus = 'spam';
341
- if (isset($_GET['post_status']) && $_GET['post_status'] == 'spam') {
342
- $newStatus = 'publish';
343
- }
344
- }
345
- // In case of Ajax request Close/Open
346
- if (isset($_GET['action']) && $_GET['action'] == 'bbp_toggle_topic_close') {
347
- $oldStatus = $post->post_status;
348
- $newStatus = 'closed';
349
- if (isset($_GET['post_status']) && $_GET['post_status'] == 'closed') {
350
- $newStatus = 'publish';
351
- }
352
- }
353
- if (!empty($newStatus) && $oldStatus != $newStatus) {
354
- $this->plugin->alerts->Trigger(8015, array(
355
- 'TopicName' => $post->post_title,
356
- 'OldStatus' => ($oldStatus == 'publish') ? 'open' : $oldStatus,
357
- 'NewStatus' => ($newStatus == 'publish') ? 'open' : $newStatus,
358
- $editorLink['name'] => $editorLink['value']
359
- ));
360
- $result = 1;
361
- }
362
- break;
363
- }
364
- return $result;
365
- }
366
-
367
- /**
368
- * Event post changed (order, parent, URL).
369
- * @param stdClass $old_post the old post
370
- * @param stdClass $new_post the new post
371
- * @return boolean $result
372
- */
373
- private function EventForumChanged($old_post, $new_post)
374
- {
375
- $editorLink = $this->GetEditorLink($new_post);
376
- // Changed Order
377
- if ($old_post->menu_order != $new_post->menu_order) {
378
- $this->plugin->alerts->Trigger(8004, array(
379
- 'ForumName' => $new_post->post_title,
380
- 'OldOrder' => $old_post->menu_order,
381
- 'NewOrder' => $new_post->menu_order,
382
- $editorLink['name'] => $editorLink['value']
383
- ));
384
- return 1;
385
- }
386
- // Changed Parent
387
- if ($old_post->post_parent != $new_post->post_parent) {
388
- switch ($old_post->post_type) {
389
- case 'forum':
390
- $this->plugin->alerts->Trigger(8008, array(
391
- 'ForumName' => $new_post->post_title,
392
- 'OldParent' => $old_post->post_parent ? get_the_title($old_post->post_parent) : 'no parent',
393
- 'NewParent' => $new_post->post_parent ? get_the_title($new_post->post_parent) : 'no parent',
394
- $editorLink['name'] => $editorLink['value']
395
- ));
396
- break;
397
- case 'topic':
398
- $this->plugin->alerts->Trigger(8018, array(
399
- 'TopicName' => $new_post->post_title,
400
- 'OldForum' => $old_post->post_parent ? get_the_title($old_post->post_parent) : 'no parent',
401
- 'NewForum' => $new_post->post_parent ? get_the_title($new_post->post_parent) : 'no parent',
402
- $editorLink['name'] => $editorLink['value']
403
- ));
404
- break;
405
- }
406
- return 1;
407
- }
408
- // Changed URL
409
- $oldLink = $this->_OldLink;
410
- $newLink = get_permalink($new_post->ID);
411
- if (!empty($oldLink) && $oldLink != $newLink) {
412
- switch ($old_post->post_type) {
413
- case 'forum':
414
- $this->plugin->alerts->Trigger(8003, array(
415
- 'ForumName' => $new_post->post_title,
416
- 'OldUrl' => $oldLink,
417
- 'NewUrl' => $newLink,
418
- $editorLink['name'] => $editorLink['value']
419
- ));
420
- break;
421
- case 'topic':
422
- $this->plugin->alerts->Trigger(8017, array(
423
- 'TopicName' => $new_post->post_title,
424
- 'OldUrl' => $oldLink,
425
- 'NewUrl' => $newLink,
426
- $editorLink['name'] => $editorLink['value']
427
- ));
428
- break;
429
- }
430
- return 1;
431
- }
432
- return 0;
433
- }
434
-
435
- /**
436
- * Trigger Event (Forum).
437
- * @param stdClass $post the post
438
- * @param integer $event event code
439
- */
440
- private function EventForumByCode($post, $event)
441
- {
442
- $editorLink = $this->GetEditorLink($post);
443
- $this->plugin->alerts->Trigger($event, array(
444
- 'ForumID' => $post->ID,
445
- 'ForumName' => $post->post_title,
446
- $editorLink['name'] => $editorLink['value']
447
- ));
448
- }
449
-
450
- /**
451
- * Trigger Event (Topic).
452
- * @param stdClass $post the post
453
- * @param integer $event event code
454
- */
455
- private function EventTopicByCode($post, $event)
456
- {
457
- $editorLink = $this->GetEditorLink($post);
458
- $this->plugin->alerts->Trigger($event, array(
459
- 'TopicID' => $post->ID,
460
- 'TopicName' => $post->post_title,
461
- $editorLink['name'] => $editorLink['value']
462
- ));
463
- }
464
-
465
- /**
466
- * Trigger of ajax events generated in the Topic Grid
467
- * @global mixed $_GET Get data
468
- */
469
- public function TriggerAjaxChange()
470
- {
471
- if (!empty($_GET['post_type']) && !empty($_GET['topic_id'])) {
472
- if ($_GET['post_type'] == 'topic') {
473
- $post = get_post($_GET['topic_id']);
474
-
475
- // Topic type
476
- if (isset($_GET['action']) && $_GET['action'] == 'bbp_toggle_topic_stick') {
477
- if (!empty($post->post_parent)) {
478
- $post_id = $post->post_parent;
479
- } else {
480
- $post_id = $_GET['topic_id'];
481
- }
482
-
483
- $bbp_sticky_topics = maybe_unserialize(get_post_meta($post_id, '_bbp_sticky_topics', true));
484
- $fn = $this->IsMultisite() ? 'get_site_option' : 'get_option';
485
- $bbp_super_sticky_topics = maybe_unserialize($fn('_bbp_super_sticky_topics'));
486
- if (!empty($bbp_sticky_topics) && in_array($_GET['topic_id'], $bbp_sticky_topics)) {
487
- $oldType = 'sticky';
488
- } elseif (!empty($bbp_super_sticky_topics) && in_array($_GET['topic_id'], $bbp_super_sticky_topics)) {
489
- $oldType = 'super sticky';
490
- } else {
491
- $oldType = 'normal';
492
- }
493
-
494
- switch ($oldType) {
495
- case 'sticky':
496
- case 'super sticky':
497
- $newType = 'normal';
498
- break;
499
- case 'normal':
500
- if (isset($_GET['super']) && $_GET['super'] == 1) {
501
- $newType = 'super sticky';
502
- } else {
503
- $newType = 'sticky';
504
- }
505
- break;
506
- }
507
- $editorLink = $this->GetEditorLink($post);
508
-
509
- if (!empty($newType) && $oldType != $newType) {
510
- $this->plugin->alerts->Trigger(8016, array(
511
- 'TopicName' => $post->post_title,
512
- 'OldType' => $oldType,
513
- 'NewType' => $newType,
514
- $editorLink['name'] => $editorLink['value']
515
- ));
516
- }
517
- }
518
- }
519
- }
520
- }
521
-
522
- /**
523
- * Get editor link.
524
- * @param stdClass $post the post
525
- * @return array $aLink name and value link
526
- */
527
- private function GetEditorLink($post)
528
- {
529
- $name = 'EditorLink';
530
- switch ($post->post_type) {
531
- case 'forum':
532
- $name .= 'Forum' ;
533
- break;
534
- case 'topic':
535
- $name .= 'Topic' ;
536
- break;
537
- }
538
- $value = get_edit_post_link($post->ID);
539
- $aLink = array(
540
- 'name' => $name,
541
- 'value' => $value,
542
- );
543
- return $aLink;
544
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  }
1
  <?php
2
  /**
3
+ * Sensor: BBPress
4
+ *
5
+ * BBPress sensor class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Support for BBPress Forum Plugin.
18
  *
19
  * 8000 User created new forum
35
  * 8020 User permanently deleted topic
36
  * 8021 User restored topic from trash
37
  * 8022 User changed visibility of a topic
38
+ *
39
+ * @package Wsal
40
+ * @subpackage Sensors
41
  */
42
+ class WSAL_Sensors_BBPress extends WSAL_AbstractSensor {
43
+
44
+ /**
45
+ * Old permalink
46
+ *
47
+ * @var string
48
+ */
49
+ protected $_OldLink = null;
50
+
51
+ /**
52
+ * Listening to events using WP hooks.
53
+ */
54
+ public function HookEvents() {
55
+ if ( current_user_can( 'edit_posts' ) ) {
56
+ add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
57
+ }
58
+ add_action( 'post_updated', array( $this, 'CheckForumChange' ), 10, 3 );
59
+ add_action( 'delete_post', array( $this, 'EventForumDeleted' ), 10, 1 );
60
+ add_action( 'wp_trash_post', array( $this, 'EventForumTrashed' ), 10, 1 );
61
+ add_action( 'untrash_post', array( $this, 'EventForumUntrashed' ) );
62
+ }
63
+
64
+ /**
65
+ * Triggered when a user accesses the admin area.
66
+ */
67
+ public function EventAdminInit() {
68
+ // Load old data, if applicable.
69
+ $this->RetrieveOldData();
70
+ // Check for Ajax changes.
71
+ $this->TriggerAjaxChange();
72
+ }
73
+
74
+ /**
75
+ * Retrieve Old data.
76
+ *
77
+ * @global mixed $_POST post data
78
+ */
79
+ protected function RetrieveOldData() {
80
+ // Filter $_POST array for security.
81
+ $post_array = filter_input_array( INPUT_POST );
82
+
83
+ if ( isset( $post_array['post_ID'] )
84
+ && isset( $post_array['_wpnonce'] )
85
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
86
+ return false;
87
+ }
88
+
89
+ if ( isset( $post_array ) && isset( $post_array['post_ID'] )
90
+ && ! ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
91
+ && ! ( isset( $post_array['action'] ) && 'autosave' === $post_array['action'] )
92
+ ) {
93
+ $post_id = intval( $post_array['post_ID'] );
94
+ $this->_OldLink = get_permalink( $post_id );
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Calls event forum changes.
100
+ *
101
+ * @param integer $post_id - Post ID.
102
+ * @param stdClass $newpost - The new post.
103
+ * @param stdClass $oldpost - The old post.
104
+ */
105
+ public function CheckForumChange( $post_id, $newpost, $oldpost ) {
106
+ if ( $this->CheckBBPress( $oldpost ) ) {
107
+ $changes = 0 + $this->EventForumCreation( $oldpost, $newpost );
108
+ // Change Visibility.
109
+ if ( ! $changes ) {
110
+ $changes = $this->EventForumChangedVisibility( $oldpost );
111
+ }
112
+ // Change Type.
113
+ if ( ! $changes ) {
114
+ $changes = $this->EventForumChangedType( $oldpost );
115
+ }
116
+ // Change status.
117
+ if ( ! $changes ) {
118
+ $changes = $this->EventForumChangedStatus( $oldpost );
119
+ }
120
+ // Change Order, Parent or URL.
121
+ if ( ! $changes ) {
122
+ $changes = $this->EventForumChanged( $oldpost, $newpost );
123
+ }
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Permanently deleted.
129
+ *
130
+ * @param integer $post_id Post ID.
131
+ */
132
+ public function EventForumDeleted( $post_id ) {
133
+ $post = get_post( $post_id );
134
+ if ( $this->CheckBBPress( $post ) ) {
135
+ switch ( $post->post_type ) {
136
+ case 'forum':
137
+ $this->EventForumByCode( $post, 8006 );
138
+ break;
139
+ case 'topic':
140
+ $this->EventTopicByCode( $post, 8020 );
141
+ break;
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Moved to Trash.
148
+ *
149
+ * @param integer $post_id Post ID.
150
+ */
151
+ public function EventForumTrashed( $post_id ) {
152
+ $post = get_post( $post_id );
153
+ if ( $this->CheckBBPress( $post ) ) {
154
+ switch ( $post->post_type ) {
155
+ case 'forum':
156
+ $this->EventForumByCode( $post, 8005 );
157
+ break;
158
+ case 'topic':
159
+ $this->EventTopicByCode( $post, 8019 );
160
+ break;
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Restored from Trash.
167
+ *
168
+ * @param integer $post_id Post ID.
169
+ */
170
+ public function EventForumUntrashed( $post_id ) {
171
+ $post = get_post( $post_id );
172
+ if ( $this->CheckBBPress( $post ) ) {
173
+ switch ( $post->post_type ) {
174
+ case 'forum':
175
+ $this->EventForumByCode( $post, 8007 );
176
+ break;
177
+ case 'topic':
178
+ $this->EventTopicByCode( $post, 8021 );
179
+ break;
180
+ }
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Check post type.
186
+ *
187
+ * @param stdClass $post Post.
188
+ * @return boolean
189
+ */
190
+ private function CheckBBPress( $post ) {
191
+ switch ( $post->post_type ) {
192
+ case 'forum':
193
+ case 'topic':
194
+ case 'reply':
195
+ return true;
196
+ default:
197
+ return false;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Event post creation.
203
+ *
204
+ * @param stdClass $old_post - The old post.
205
+ * @param stdClass $new_post - The new post.
206
+ * @return boolean
207
+ */
208
+ private function EventForumCreation( $old_post, $new_post ) {
209
+ // Filter $_POST array for security.
210
+ $post_array = filter_input_array( INPUT_POST );
211
+
212
+ if ( isset( $post_array['post_ID'] )
213
+ && isset( $post_array['_wpnonce'] )
214
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
215
+ return false;
216
+ }
217
+
218
+ $original = isset( $post_array['original_post_status'] ) ? $post_array['original_post_status'] : '';
219
+ if ( 'draft' === $old_post->post_status || 'auto-draft' === $original ) {
220
+ $editor_link = $this->GetEditorLink( $new_post );
221
+ if ( 'publish' === $new_post->post_status ) {
222
+ switch ( $old_post->post_type ) {
223
+ case 'forum':
224
+ $this->plugin->alerts->Trigger(
225
+ 8000, array(
226
+ 'ForumName' => $new_post->post_title,
227
+ 'ForumURL' => get_permalink( $new_post->ID ),
228
+ $editor_link['name'] => $editor_link['value'],
229
+ )
230
+ );
231
+ break;
232
+ case 'topic':
233
+ $this->plugin->alerts->Trigger(
234
+ 8014, array(
235
+ 'TopicName' => $new_post->post_title,
236
+ 'TopicURL' => get_permalink( $new_post->ID ),
237
+ $editor_link['name'] => $editor_link['value'],
238
+ )
239
+ );
240
+ break;
241
+ }
242
+ return 1;
243
+ }
244
+ }
245
+ return 0;
246
+ }
247
+
248
+ /**
249
+ * Event post changed visibility.
250
+ *
251
+ * @param stdClass $post The post.
252
+ * @return boolean $result
253
+ */
254
+ private function EventForumChangedVisibility( $post ) {
255
+ // Filter $_POST array for security.
256
+ $post_array = filter_input_array( INPUT_POST );
257
+
258
+ if ( isset( $post_array['post_ID'] )
259
+ && isset( $post_array['_wpnonce'] )
260
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
261
+ return false;
262
+ }
263
+
264
+ $result = 0;
265
+ $editor_link = $this->GetEditorLink( $post );
266
+ switch ( $post->post_type ) {
267
+ case 'forum':
268
+ $old_visibility = ! empty( $post_array['visibility'] ) ? $post_array['visibility'] : '';
269
+ $new_visibility = ! empty( $post_array['bbp_forum_visibility'] ) ? $post_array['bbp_forum_visibility'] : '';
270
+ $new_visibility = ( 'publish' === $new_visibility ) ? 'public' : $new_visibility;
271
+
272
+ if ( ! empty( $new_visibility ) && 'auto-draft' != $old_visibility && $old_visibility != $new_visibility ) {
273
+ $this->plugin->alerts->Trigger(
274
+ 8002, array(
275
+ 'ForumName' => $post->post_title,
276
+ 'OldVisibility' => $old_visibility,
277
+ 'NewVisibility' => $new_visibility,
278
+ $editor_link['name'] => $editor_link['value'],
279
+ )
280
+ );
281
+ $result = 1;
282
+ }
283
+ break;
284
+ case 'topic':
285
+ $old_visibility = ! empty( $post_array['hidden_post_visibility'] ) ? $post_array['hidden_post_visibility'] : '';
286
+ $new_visibility = ! empty( $post_array['visibility'] ) ? $post_array['visibility'] : '';
287
+ $new_visibility = ( 'password' === $new_visibility ) ? 'password protected' : $new_visibility;
288
+
289
+ if ( ! empty( $new_visibility ) && 'auto-draft' != $old_visibility && $old_visibility != $new_visibility ) {
290
+ $this->plugin->alerts->Trigger(
291
+ 8022, array(
292
+ 'TopicName' => $post->post_title,
293
+ 'OldVisibility' => $old_visibility,
294
+ 'NewVisibility' => $new_visibility,
295
+ $editor_link['name'] => $editor_link['value'],
296
+ )
297
+ );
298
+ $result = 1;
299
+ }
300
+ break;
301
+ }
302
+ return $result;
303
+ }
304
+
305
+ /**
306
+ * Event post changed type.
307
+ *
308
+ * @param stdClass $post The post.
309
+ * @return boolean $result
310
+ */
311
+ private function EventForumChangedType( $post ) {
312
+ // Filter $_POST array for security.
313
+ $post_array = filter_input_array( INPUT_POST );
314
+
315
+ if ( isset( $post_array['post_ID'] )
316
+ && isset( $post_array['_wpnonce'] )
317
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
318
+ return false;
319
+ }
320
+
321
+ $result = 0;
322
+ $editor_link = $this->GetEditorLink( $post );
323
+ switch ( $post->post_type ) {
324
+ case 'forum':
325
+ $bbp_forum_type = get_post_meta( $post->ID, '_bbp_forum_type', true );
326
+ $old_type = ! empty( $bbp_forum_type ) ? $bbp_forum_type : 'forum';
327
+ $new_type = ! empty( $post_array['bbp_forum_type'] ) ? $post_array['bbp_forum_type'] : '';
328
+ if ( ! empty( $new_type ) && $old_type != $new_type ) {
329
+ $this->plugin->alerts->Trigger(
330
+ 8011, array(
331
+ 'ForumName' => $post->post_title,
332
+ 'OldType' => $old_type,
333
+ 'NewType' => $new_type,
334
+ $editor_link['name'] => $editor_link['value'],
335
+ )
336
+ );
337
+ $result = 1;
338
+ }
339
+ break;
340
+ case 'topic':
341
+ if ( ! empty( $post_array['parent_id'] ) ) {
342
+ $post_id = $post_array['parent_id'];
343
+ } else {
344
+ $post_id = $post->ID;
345
+ }
346
+ $bbp_sticky_topics = maybe_unserialize( get_post_meta( $post_id, '_bbp_sticky_topics', true ) );
347
+ $fn = $this->IsMultisite() ? 'get_site_option' : 'get_option';
348
+ $bbp_super_sticky_topics = maybe_unserialize( $fn( '_bbp_super_sticky_topics' ) );
349
+ if ( ! empty( $bbp_sticky_topics ) && in_array( $post->ID, $bbp_sticky_topics ) ) {
350
+ $old_type = 'sticky';
351
+ } elseif ( ! empty( $bbp_super_sticky_topics ) && in_array( $post->ID, $bbp_super_sticky_topics ) ) {
352
+ $old_type = 'super';
353
+ } else {
354
+ $old_type = 'unstick';
355
+ }
356
+ $new_type = ! empty( $post_array['bbp_stick_topic'] ) ? $post_array['bbp_stick_topic'] : '';
357
+ if ( ! empty( $new_type ) && $old_type != $new_type ) {
358
+ $this->plugin->alerts->Trigger(
359
+ 8016, array(
360
+ 'TopicName' => $post->post_title,
361
+ 'OldType' => ( 'unstick' == $old_type ) ? 'normal' : (( 'super' == $old_type ) ? 'super sticky' : $old_type),
362
+ 'NewType' => ( 'unstick' == $new_type ) ? 'normal' : (( 'super' == $new_type ) ? 'super sticky' : $new_type),
363
+ $editor_link['name'] => $editor_link['value'],
364
+ )
365
+ );
366
+ $result = 1;
367
+ }
368
+ break;
369
+ }
370
+ return $result;
371
+ }
372
+
373
+ /**
374
+ * Event post changed status.
375
+ *
376
+ * @param stdClass $post The post.
377
+ * @return boolean $result
378
+ */
379
+ private function EventForumChangedStatus( $post ) {
380
+ // Filter $_POST and $_GET array for security.
381
+ $post_array = filter_input_array( INPUT_POST );
382
+ $get_array = filter_input_array( INPUT_GET );
383
+
384
+ if ( isset( $post_array['post_ID'] )
385
+ && isset( $post_array['_wpnonce'] )
386
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
387
+ return false;
388
+ }
389
+
390
+ $result = 0;
391
+ $editor_link = $this->GetEditorLink( $post );
392
+ switch ( $post->post_type ) {
393
+ case 'forum':
394
+ $bbp_status = get_post_meta( $post->ID, '_bbp_status', true );
395
+ $old_status = ! empty( $bbp_status ) ? $bbp_status : 'open';
396
+ $new_status = ! empty( $post_array['bbp_forum_status'] ) ? $post_array['bbp_forum_status'] : '';
397
+ if ( ! empty( $new_status ) && $old_status !== $new_status ) {
398
+ $this->plugin->alerts->Trigger(
399
+ 8001, array(
400
+ 'ForumName' => $post->post_title,
401
+ 'OldStatus' => $old_status,
402
+ 'NewStatus' => $new_status,
403
+ $editor_link['name'] => $editor_link['value'],
404
+ )
405
+ );
406
+ $result = 1;
407
+ }
408
+ break;
409
+ case 'topic':
410
+ $old_status = ! empty( $post_array['original_post_status'] ) ? $post_array['original_post_status'] : '';
411
+ $new_status = ! empty( $post_array['post_status'] ) ? $post_array['post_status'] : '';
412
+ // In case of Ajax request Spam/Not spam.
413
+ if ( isset( $get_array['action'] ) && 'bbp_toggle_topic_spam' === $get_array['action'] ) {
414
+ $old_status = $post->post_status;
415
+ $new_status = 'spam';
416
+ if ( isset( $get_array['post_status'] ) && 'spam' === $get_array['post_status'] ) {
417
+ $new_status = 'publish';
418
+ }
419
+ }
420
+ // In case of Ajax request Close/Open.
421
+ if ( isset( $get_array['action'] ) && 'bbp_toggle_topic_close' === $get_array['action'] ) {
422
+ $old_status = $post->post_status;
423
+ $new_status = 'closed';
424
+ if ( isset( $get_array['post_status'] ) && 'closed' === $get_array['post_status'] ) {
425
+ $new_status = 'publish';
426
+ }
427
+ }
428
+ if ( ! empty( $new_status ) && $old_status !== $new_status ) {
429
+ $this->plugin->alerts->Trigger(
430
+ 8015, array(
431
+ 'TopicName' => $post->post_title,
432
+ 'OldStatus' => ( 'publish' === $old_status ) ? 'open' : $old_status,
433
+ 'NewStatus' => ( 'publish' === $new_status ) ? 'open' : $new_status,
434
+ $editor_link['name'] => $editor_link['value'],
435
+ )
436
+ );
437
+ $result = 1;
438
+ }
439
+ break;
440
+ }
441
+ return $result;
442
+ }
443
+
444
+ /**
445
+ * Event post changed (order, parent, URL).
446
+ *
447
+ * @param stdClass $old_post - The old post.
448
+ * @param stdClass $new_post - The new post.
449
+ * @return boolean $result
450
+ */
451
+ private function EventForumChanged( $old_post, $new_post ) {
452
+ $editor_link = $this->GetEditorLink( $new_post );
453
+ // Changed Order.
454
+ if ( $old_post->menu_order != $new_post->menu_order ) {
455
+ $this->plugin->alerts->Trigger(
456
+ 8004, array(
457
+ 'ForumName' => $new_post->post_title,
458
+ 'OldOrder' => $old_post->menu_order,
459
+ 'NewOrder' => $new_post->menu_order,
460
+ $editor_link['name'] => $editor_link['value'],
461
+ )
462
+ );
463
+ return 1;
464
+ }
465
+ // Changed Parent.
466
+ if ( $old_post->post_parent != $new_post->post_parent ) {
467
+ switch ( $old_post->post_type ) {
468
+ case 'forum':
469
+ $this->plugin->alerts->Trigger(
470
+ 8008, array(
471
+ 'ForumName' => $new_post->post_title,
472
+ 'OldParent' => $old_post->post_parent ? get_the_title( $old_post->post_parent ) : 'no parent',
473
+ 'NewParent' => $new_post->post_parent ? get_the_title( $new_post->post_parent ) : 'no parent',
474
+ $editor_link['name'] => $editor_link['value'],
475
+ )
476
+ );
477
+ break;
478
+ case 'topic':
479
+ $this->plugin->alerts->Trigger(
480
+ 8018, array(
481
+ 'TopicName' => $new_post->post_title,
482
+ 'OldForum' => $old_post->post_parent ? get_the_title( $old_post->post_parent ) : 'no parent',
483
+ 'NewForum' => $new_post->post_parent ? get_the_title( $new_post->post_parent ) : 'no parent',
484
+ $editor_link['name'] => $editor_link['value'],
485
+ )
486
+ );
487
+ break;
488
+ }
489
+ return 1;
490
+ }
491
+ // Changed URL.
492
+ $old_link = $this->_OldLink;
493
+ $new_link = get_permalink( $new_post->ID );
494
+ if ( ! empty( $old_link ) && $old_link != $new_link ) {
495
+ switch ( $old_post->post_type ) {
496
+ case 'forum':
497
+ $this->plugin->alerts->Trigger(
498
+ 8003, array(
499
+ 'ForumName' => $new_post->post_title,
500
+ 'OldUrl' => $old_link,
501
+ 'NewUrl' => $new_link,
502
+ $editor_link['name'] => $editor_link['value'],
503
+ )
504
+ );
505
+ break;
506
+ case 'topic':
507
+ $this->plugin->alerts->Trigger(
508
+ 8017, array(
509
+ 'TopicName' => $new_post->post_title,
510
+ 'OldUrl' => $old_link,
511
+ 'NewUrl' => $new_link,
512
+ $editor_link['name'] => $editor_link['value'],
513
+ )
514
+ );
515
+ break;
516
+ }
517
+ return 1;
518
+ }
519
+ return 0;
520
+ }
521
+
522
+ /**
523
+ * Trigger Event (Forum).
524
+ *
525
+ * @param stdClass $post - The post.
526
+ * @param integer $event - Event code.
527
+ */
528
+ private function EventForumByCode( $post, $event ) {
529
+ $editor_link = $this->GetEditorLink( $post );
530
+ $this->plugin->alerts->Trigger(
531
+ $event, array(
532
+ 'ForumID' => $post->ID,
533
+ 'ForumName' => $post->post_title,
534
+ $editor_link['name'] => $editor_link['value'],
535
+ )
536
+ );
537
+ }
538
+
539
+ /**
540
+ * Trigger Event (Topic).
541
+ *
542
+ * @param stdClass $post - The post.
543
+ * @param integer $event - Event code.
544
+ */
545
+ private function EventTopicByCode( $post, $event ) {
546
+ $editor_link = $this->GetEditorLink( $post );
547
+ $this->plugin->alerts->Trigger(
548
+ $event, array(
549
+ 'TopicID' => $post->ID,
550
+ 'TopicName' => $post->post_title,
551
+ $editor_link['name'] => $editor_link['value'],
552
+ )
553
+ );
554
+ }
555
+
556
+ /**
557
+ * Trigger of ajax events generated in the Topic Grid
558
+ *
559
+ * @global mixed $_GET Get data
560
+ */
561
+ public function TriggerAjaxChange() {
562
+ // Filter $_GET array for security.
563
+ $get_array = filter_input_array( INPUT_GET );
564
+
565
+ if ( ! empty( $get_array['post_type'] ) && ! empty( $get_array['topic_id'] ) ) {
566
+ if ( 'topic' == $get_array['post_type'] ) {
567
+ $post = get_post( $get_array['topic_id'] );
568
+
569
+ // Topic type.
570
+ if ( isset( $get_array['action'] ) && 'bbp_toggle_topic_stick' == $get_array['action'] ) {
571
+ if ( ! empty( $post->post_parent ) ) {
572
+ $post_id = $post->post_parent;
573
+ } else {
574
+ $post_id = $get_array['topic_id'];
575
+ }
576
+
577
+ $bbp_sticky_topics = maybe_unserialize( get_post_meta( $post_id, '_bbp_sticky_topics', true ) );
578
+ $fn = $this->IsMultisite() ? 'get_site_option' : 'get_option';
579
+ $bbp_super_sticky_topics = maybe_unserialize( $fn( '_bbp_super_sticky_topics' ) );
580
+ if ( ! empty( $bbp_sticky_topics ) && in_array( $get_array['topic_id'], $bbp_sticky_topics ) ) {
581
+ $old_type = 'sticky';
582
+ } elseif ( ! empty( $bbp_super_sticky_topics ) && in_array( $get_array['topic_id'], $bbp_super_sticky_topics ) ) {
583
+ $old_type = 'super sticky';
584
+ } else {
585
+ $old_type = 'normal';
586
+ }
587
+
588
+ switch ( $old_type ) {
589
+ case 'sticky':
590
+ case 'super sticky':
591
+ $new_type = 'normal';
592
+ break;
593
+ case 'normal':
594
+ if ( isset( $get_array['super'] ) && 1 == $get_array['super'] ) {
595
+ $new_type = 'super sticky';
596
+ } else {
597
+ $new_type = 'sticky';
598
+ }
599
+ break;
600
+ }
601
+ $editor_link = $this->GetEditorLink( $post );
602
+
603
+ if ( ! empty( $new_type ) && $old_type != $new_type ) {
604
+ $this->plugin->alerts->Trigger(
605
+ 8016, array(
606
+ 'TopicName' => $post->post_title,
607
+ 'OldType' => $old_type,
608
+ 'NewType' => $new_type,
609
+ $editor_link['name'] => $editor_link['value'],
610
+ )
611
+ );
612
+ }
613
+ }
614
+ }
615
+ }
616
+ }
617
+
618
+ /**
619
+ * Get editor link.
620
+ *
621
+ * @param stdClass $post - The post.
622
+ * @return array $editor_link_array - Name and value link.
623
+ */
624
+ private function GetEditorLink( $post ) {
625
+ $name = 'EditorLink';
626
+ switch ( $post->post_type ) {
627
+ case 'forum':
628
+ $name .= 'Forum' ;
629
+ break;
630
+ case 'topic':
631
+ $name .= 'Topic' ;
632
+ break;
633
+ }
634
+ $value = get_edit_post_link( $post->ID );
635
+ $editor_link_array = array(
636
+ 'name' => $name,
637
+ 'value' => $value,
638
+ );
639
+ return $editor_link_array;
640
+ }
641
  }
classes/Sensors/Comments.php CHANGED
@@ -1,8 +1,20 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
5
- * Wordpress comments.
 
 
 
 
 
 
 
6
  *
7
  * 2090 User approved a comment
8
  * 2091 User unapproved a comment
@@ -14,173 +26,191 @@
14
  * 2097 User restored a comment from the trash
15
  * 2098 User permanently deleted a comment
16
  * 2099 User posted a comment
 
 
 
17
  */
18
- class WSAL_Sensors_Comments extends WSAL_AbstractSensor
19
- {
20
- /**
21
- * Listening to events using WP hooks.
22
- */
23
- public function HookEvents()
24
- {
25
- add_action('edit_comment', array($this, 'EventCommentEdit'), 10, 1);
26
- add_action('transition_comment_status', array($this, 'EventCommentApprove'), 10, 3);
27
- add_action('spammed_comment', array($this, 'EventCommentSpam'), 10, 1);
28
- add_action('unspammed_comment', array($this, 'EventCommentUnspam'), 10, 1);
29
- add_action('trashed_comment', array($this, 'EventCommentTrash'), 10, 1);
30
- add_action('untrashed_comment', array($this, 'EventCommentUntrash'), 10, 1);
31
- add_action('deleted_comment', array($this, 'EventCommentDeleted'), 10, 1);
32
- add_action('comment_post', array($this, 'EventComment'), 10, 2);
33
- }
34
-
35
- /**
36
- * Trigger comment edit.
37
- * @param integer $comment_ID comment ID
38
- */
39
- public function EventCommentEdit($comment_ID)
40
- {
41
- $comment = get_comment($comment_ID);
42
- $this->EventGeneric($comment_ID, 2093);
43
- }
44
-
45
- /**
46
- * Trigger comment status.
47
- * @param string $new_status new status
48
- * @param string $old_status old status
49
- * @param stdClass $comment comment
50
- */
51
- public function EventCommentApprove($new_status, $old_status, $comment)
52
- {
53
- if (!empty($comment) && $old_status != $new_status) {
54
- $post = get_post($comment->comment_post_ID);
55
- $comment_link = get_permalink($post->ID) . "#comment-" . $comment->comment_ID;
56
- $fields = array(
57
- 'PostTitle' => $post->post_title,
58
- 'Author' => $comment->comment_author,
59
- 'Date' => $comment->comment_date,
60
- 'CommentLink' => '<a target="_blank" href="' . $comment_link . '">' . $comment->comment_date . '</a>'
61
- );
62
-
63
- if ($new_status == 'approved') {
64
- $this->plugin->alerts->Trigger(2090, $fields);
65
- }
66
- if ($new_status == 'unapproved') {
67
- $this->plugin->alerts->Trigger(2091, $fields);
68
- }
69
- }
70
- }
71
-
72
- /**
73
- * Trigger comment spam.
74
- * @param integer $comment_ID comment ID
75
- */
76
- public function EventCommentSpam($comment_ID)
77
- {
78
- $this->EventGeneric($comment_ID, 2094);
79
- }
80
-
81
- /**
82
- * Trigger comment unspam.
83
- * @param integer $comment_ID comment ID
84
- */
85
- public function EventCommentUnspam($comment_ID)
86
- {
87
- $this->EventGeneric($comment_ID, 2095);
88
- }
89
-
90
- /**
91
- * Trigger comment trash.
92
- * @param integer $comment_ID comment ID
93
- */
94
- public function EventCommentTrash($comment_ID)
95
- {
96
- $this->EventGeneric($comment_ID, 2096);
97
- }
98
-
99
- /**
100
- * Trigger comment untrash.
101
- * @param integer $comment_ID comment ID
102
- */
103
- public function EventCommentUntrash($comment_ID)
104
- {
105
- $this->EventGeneric($comment_ID, 2097);
106
- }
107
-
108
- /**
109
- * Trigger comment deleted.
110
- * @param integer $comment_ID comment ID
111
- */
112
- public function EventCommentDeleted($comment_ID)
113
- {
114
- $this->EventGeneric($comment_ID, 2098);
115
- }
116
-
117
- /**
118
- * Fires immediately after a comment is inserted into the database.
119
- * @param int $comment_ID The comment ID.
120
- * @param int|string $comment_approved 1 if the comment is approved, 0 if not, 'spam' if spam.
121
- */
122
- public function EventComment($comment_ID, $comment_approved = null)
123
- {
124
- if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'replyto-comment') {
125
- $this->EventGeneric($comment_ID, 2092);
126
- }
127
- if (isset($_REQUEST['comment'])) {
128
- $comment = get_comment($comment_ID);
129
- if (!empty($comment)) {
130
- if ($comment->comment_approved != 'spam') {
131
- $post = get_post($comment->comment_post_ID);
132
- $comment_link = get_permalink($post->ID) . "#comment-" . $comment_ID;
133
- $fields = array(
134
- 'Date' => $comment->comment_date,
135
- 'CommentLink' => '<a target="_blank" href="' . $comment_link . '">' . $comment->comment_date . '</a>'
136
- );
137
- if (!username_exists($comment->comment_author)) {
138
- $fields['CommentMsg'] = sprintf("A comment was posted in response to the post <strong>%s</strong>. The comment was posted by <strong>%s</strong>", $post->post_title, $this->CheckAuthor($comment));
139
- $fields['Username'] = "Website Visitor";
140
- } else {
141
- $fields['CommentMsg'] = sprintf("Posted a comment in response to the post <strong>%s</strong>", $post->post_title);
142
- }
143
-
144
- $this->plugin->alerts->Trigger(2099, $fields);
145
- }
146
- }
147
- }
148
- }
149
-
150
- /**
151
- * Trigger generic event.
152
- * @param integer $comment_ID comment ID
153
- * @param integer $alert_code event code
154
- */
155
- private function EventGeneric($comment_ID, $alert_code)
156
- {
157
- $comment = get_comment($comment_ID);
158
- if (!empty($comment)) {
159
- $post = get_post($comment->comment_post_ID);
160
- $comment_link = get_permalink($post->ID) . "#comment-" . $comment_ID;
161
- $fields = array(
162
- 'PostTitle' => $post->post_title,
163
- 'Author' => $comment->comment_author,
164
- 'Date' => $comment->comment_date,
165
- 'CommentLink' => '<a target="_blank" href="' . $comment_link . '">' . $comment->comment_date . '</a>'
166
- );
167
-
168
- $this->plugin->alerts->Trigger($alert_code, $fields);
169
- }
170
- }
171
-
172
- /**
173
- * Shows the username if the comment is owned by a user
174
- * and the email if the comment was posted by a non WordPress user
175
- * @param stdClass $comment comment
176
- * @return string author username or email
177
- */
178
- private function CheckAuthor($comment)
179
- {
180
- if (username_exists($comment->comment_author)) {
181
- return $comment->comment_author;
182
- } else {
183
- return $comment->comment_author_email;
184
- }
185
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }
1
  <?php
2
  /**
3
+ * Sensor: Comments
4
+ *
5
+ * Comments sensor class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * WordPress Comments.
18
  *
19
  * 2090 User approved a comment
20
  * 2091 User unapproved a comment
26
  * 2097 User restored a comment from the trash
27
  * 2098 User permanently deleted a comment
28
  * 2099 User posted a comment
29
+ *
30
+ * @package Wsal
31
+ * @subpackage Sensors
32
  */
33
+ class WSAL_Sensors_Comments extends WSAL_AbstractSensor {
34
+
35
+ /**
36
+ * Listening to events using WP hooks.
37
+ */
38
+ public function HookEvents() {
39
+ add_action( 'edit_comment', array( $this, 'EventCommentEdit' ), 10, 1 );
40
+ add_action( 'transition_comment_status', array( $this, 'EventCommentApprove' ), 10, 3 );
41
+ add_action( 'spammed_comment', array( $this, 'EventCommentSpam' ), 10, 1 );
42
+ add_action( 'unspammed_comment', array( $this, 'EventCommentUnspam' ), 10, 1 );
43
+ add_action( 'trashed_comment', array( $this, 'EventCommentTrash' ), 10, 1 );
44
+ add_action( 'untrashed_comment', array( $this, 'EventCommentUntrash' ), 10, 1 );
45
+ add_action( 'deleted_comment', array( $this, 'EventCommentDeleted' ), 10, 1 );
46
+ add_action( 'comment_post', array( $this, 'EventComment' ), 10, 2 );
47
+ }
48
+
49
+ /**
50
+ * Trigger comment edit.
51
+ *
52
+ * @param integer $comment_id - Comment ID.
53
+ */
54
+ public function EventCommentEdit( $comment_id ) {
55
+ $comment = get_comment( $comment_id );
56
+ $this->EventGeneric( $comment_id, 2093 );
57
+ }
58
+
59
+ /**
60
+ * Trigger comment status.
61
+ *
62
+ * @param string $new_status - New status.
63
+ * @param string $old_status - Old status.
64
+ * @param stdClass $comment - Comment.
65
+ */
66
+ public function EventCommentApprove( $new_status, $old_status, $comment ) {
67
+ if ( ! empty( $comment ) && $old_status != $new_status ) {
68
+ $post = get_post( $comment->comment_post_ID );
69
+ $comment_link = get_permalink( $post->ID ) . '#comment-' . $comment->comment_ID;
70
+ $fields = array(
71
+ 'PostTitle' => $post->post_title,
72
+ 'Author' => $comment->comment_author,
73
+ 'Date' => $comment->comment_date,
74
+ 'CommentLink' => '<a target="_blank" href="' . $comment_link . '">' . $comment->comment_date . '</a>',
75
+ );
76
+
77
+ if ( 'approved' == $new_status ) {
78
+ $this->plugin->alerts->Trigger( 2090, $fields );
79
+ }
80
+ if ( 'unapproved' == $new_status ) {
81
+ $this->plugin->alerts->Trigger( 2091, $fields );
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Trigger comment spam.
88
+ *
89
+ * @param integer $comment_id - Comment ID.
90
+ */
91
+ public function EventCommentSpam( $comment_id ) {
92
+ $this->EventGeneric( $comment_id, 2094 );
93
+ }
94
+
95
+ /**
96
+ * Trigger comment unspam.
97
+ *
98
+ * @param integer $comment_id - Comment ID.
99
+ */
100
+ public function EventCommentUnspam( $comment_id ) {
101
+ $this->EventGeneric( $comment_id, 2095 );
102
+ }
103
+
104
+ /**
105
+ * Trigger comment trash.
106
+ *
107
+ * @param integer $comment_id - Comment ID.
108
+ */
109
+ public function EventCommentTrash( $comment_id ) {
110
+ $this->EventGeneric( $comment_id, 2096 );
111
+ }
112
+
113
+ /**
114
+ * Trigger comment untrash.
115
+ *
116
+ * @param integer $comment_id comment ID.
117
+ */
118
+ public function EventCommentUntrash( $comment_id ) {
119
+ $this->EventGeneric( $comment_id, 2097 );
120
+ }
121
+
122
+ /**
123
+ * Trigger comment deleted.
124
+ *
125
+ * @param integer $comment_id comment ID.
126
+ */
127
+ public function EventCommentDeleted( $comment_id ) {
128
+ $this->EventGeneric( $comment_id, 2098 );
129
+ }
130
+
131
+ /**
132
+ * Fires immediately after a comment is inserted into the database.
133
+ *
134
+ * @param int $comment_id The comment ID.
135
+ * @param int|string $comment_approved 1 if the comment is approved, 0 if not, 'spam' if spam.
136
+ */
137
+ public function EventComment( $comment_id, $comment_approved = null ) {
138
+ // Filter $_POST array for security.
139
+ $post_array = filter_input_array( INPUT_POST );
140
+
141
+ if ( isset( $post_array['action'] ) && 'replyto-comment' == $post_array['action'] ) {
142
+ $this->EventGeneric( $comment_id, 2092 );
143
+ }
144
+ if ( isset( $post_array['comment'] ) ) {
145
+ $comment = get_comment( $comment_id );
146
+ if ( ! empty( $comment ) ) {
147
+ if ( 'spam' != $comment->comment_approved ) {
148
+ $post = get_post( $comment->comment_post_ID );
149
+ $comment_link = get_permalink( $post->ID ) . '#comment-' . $comment_id;
150
+ $fields = array(
151
+ 'Date' => $comment->comment_date,
152
+ 'CommentLink' => '<a target="_blank" href="' . $comment_link . '">' . $comment->comment_date . '</a>',
153
+ );
154
+ if ( ! username_exists( $comment->comment_author ) ) {
155
+ // Set the fields.
156
+ $fields['CommentMsg'] = sprintf( 'A comment was posted in response to the post <strong>%s</strong>. The comment was posted by <strong>%s</strong>', $post->post_title, $this->CheckAuthor( $comment ) );
157
+ $fields['Username'] = 'Website Visitor';
158
+ } else {
159
+ // Get user roles.
160
+ $user_data = get_user_by( 'login', $comment->comment_author );
161
+ $user_roles = $user_data->roles;
162
+
163
+ // Check if superadmin.
164
+ if ( function_exists( 'is_super_admin' ) && is_super_admin() ) {
165
+ $user_roles[] = 'superadmin';
166
+ }
167
+
168
+ // Set the fields.
169
+ $fields['Username'] = $comment->comment_author;
170
+ $fields['CurrentUserRoles'] = $user_roles;
171
+ $fields['CommentMsg'] = sprintf( 'Posted a comment in response to the post <strong>%s</strong>', $post->post_title );
172
+ }
173
+
174
+ $this->plugin->alerts->Trigger( 2099, $fields );
175
+ }
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Trigger generic event.
182
+ *
183
+ * @param integer $comment_id - Comment ID.
184
+ * @param integer $alert_code - Event code.
185
+ */
186
+ private function EventGeneric( $comment_id, $alert_code ) {
187
+ $comment = get_comment( $comment_id );
188
+ if ( ! empty( $comment ) ) {
189
+ $post = get_post( $comment->comment_post_ID );
190
+ $comment_link = get_permalink( $post->ID ) . '#comment-' . $comment_id;
191
+ $fields = array(
192
+ 'PostTitle' => $post->post_title,
193
+ 'Author' => $comment->comment_author,
194
+ 'Date' => $comment->comment_date,
195
+ 'CommentLink' => '<a target="_blank" href="' . $comment_link . '">' . $comment->comment_date . '</a>',
196
+ );
197
+
198
+ $this->plugin->alerts->Trigger( $alert_code, $fields );
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Shows the username if the comment is owned by a user
204
+ * and the email if the comment was posted by a non WordPress user
205
+ *
206
+ * @param stdClass $comment - Comment.
207
+ * @return string - Author username or email.
208
+ */
209
+ private function CheckAuthor( $comment ) {
210
+ if ( username_exists( $comment->comment_author ) ) {
211
+ return $comment->comment_author;
212
+ } else {
213
+ return $comment->comment_author_email;
214
+ }
215
+ }
216
  }
classes/Sensors/Content.php CHANGED
@@ -1,8 +1,20 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
5
- * Wordpress contents (posts, pages and custom posts).
 
 
 
 
 
 
 
6
  *
7
  * 2000 User created a new blog post and saved it as draft
8
  * 2001 User published a blog post
@@ -84,22 +96,32 @@
84
  * 2123 User renamed tag
85
  * 2124 User changed tag slug
86
  * 2125 User changed tag description
 
 
 
87
  */
88
  class WSAL_Sensors_Content extends WSAL_AbstractSensor {
 
89
  /**
90
- * @var stdClass old post
 
 
91
  */
92
- protected $_OldPost = null;
93
 
94
  /**
95
- * @var string old permalink
 
 
96
  */
97
- protected $_OldLink = null;
98
 
99
  /**
100
- * @var array old categories
 
 
101
  */
102
- protected $_OldCats = null;
103
 
104
  /**
105
  * Old tags.
@@ -109,36 +131,39 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
109
  protected $_old_tags = null;
110
 
111
  /**
112
- * @var string old path to file
 
 
113
  */
114
- protected $_OldTmpl = null;
115
 
116
  /**
117
- * @var boolean old post is marked as sticky
 
 
118
  */
119
- protected $_OldStky = null;
120
 
121
  /**
122
  * Listening to events using WP hooks.
123
  */
124
- public function HookEvents()
125
- {
126
- if (current_user_can("edit_posts")) {
127
- add_action('admin_init', array($this, 'EventWordpressInit'));
128
- }
129
- add_action('transition_post_status', array($this, 'EventPostChanged'), 10, 3);
130
- add_action('delete_post', array($this, 'EventPostDeleted'), 10, 1);
131
- add_action('wp_trash_post', array($this, 'EventPostTrashed'), 10, 1);
132
- add_action('untrash_post', array($this, 'EventPostUntrashed'));
133
- add_action('edit_category', array($this, 'EventChangedCategoryParent'));
134
- add_action('save_post', array($this, 'SetRevisionLink'), 10, 3);
135
- add_action('publish_future_post', array($this, 'EventPublishFuture'), 10, 1);
136
-
137
- add_action('create_category', array($this, 'EventCategoryCreation'), 10, 1);
138
  add_action( 'create_post_tag', array( $this, 'EventTagCreation' ), 10, 1 );
139
 
140
  add_action( 'wp_head', array( $this, 'ViewingPost' ), 10 );
141
- add_filter('post_edit_form_tag', array($this, 'EditingPost'), 10, 1);
142
 
143
  add_filter( 'wp_update_term_data', array( $this, 'event_terms_rename' ), 10, 4 );
144
  }
@@ -153,7 +178,6 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
153
  * @since 2.6.9
154
  */
155
  public function event_terms_rename( $data, $term_id, $taxonomy, $args ) {
156
-
157
  // Check if the taxonomy is term.
158
  if ( 'post_tag' !== $taxonomy ) {
159
  return $data;
@@ -169,29 +193,39 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
169
  $old_name = $term->name;
170
  $old_slug = $term->slug;
171
  $old_desc = $term->description;
 
172
 
173
  // Update if both names are not same.
174
  if ( $old_name !== $new_name ) {
175
- $this->plugin->alerts->Trigger( 2123, array(
176
- 'old_name' => $old_name,
177
- 'new_name' => $new_name,
178
- ) );
 
 
 
179
  }
180
 
181
  // Update if both slugs are not same.
182
  if ( $old_slug !== $new_slug ) {
183
- $this->plugin->alerts->Trigger( 2124, array(
184
- 'tag' => $new_name,
185
- 'old_slug' => $old_slug,
186
- 'new_slug' => $new_slug,
187
- ) );
 
 
 
188
  }
189
 
190
  // Update if both descriptions are not same.
191
  if ( $old_desc !== $new_desc ) {
192
- $this->plugin->alerts->Trigger( 2125, array(
193
- 'tag' => $new_name,
194
- ) );
 
 
 
195
  }
196
  return $data;
197
 
@@ -199,32 +233,32 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
199
 
200
  /**
201
  * Gets the alert code based on the type of post.
202
- * @param stdClass $post the post
203
- * @param integer $typePost alert code type post
204
- * @param integer $typePage alert code type page
205
- * @param integer $typeCustom alert code type custom
206
- * @return integer alert code
 
207
  */
208
- protected function GetEventTypeForPostType($post, $typePost, $typePage, $typeCustom)
209
- {
210
- switch ($post->post_type) {
211
  case 'page':
212
- return $typePage;
213
  case 'post':
214
- return $typePost;
215
  default:
216
- return $typeCustom;
217
  }
218
  }
219
 
220
  /**
221
  * Triggered when a user accesses the admin area.
222
  */
223
- public function EventWordpressInit()
224
- {
225
- // load old data, if applicable
226
  $this->RetrieveOldData();
227
- // check for category changes
 
228
  $this->CheckCategoryDeletion();
229
 
230
  // Check for tag changes.
@@ -233,125 +267,195 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
233
 
234
  /**
235
  * Retrieve Old data.
236
- * @global mixed $_POST post data
 
237
  */
238
- protected function RetrieveOldData()
239
- {
240
- if (isset($_POST) && isset($_POST['post_ID'])
241
- && !(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
242
- && !(isset($_POST['action']) && $_POST['action'] == 'autosave')
243
- ) {
244
- $postID = intval($_POST['post_ID']);
245
- $this->_OldPost = get_post($postID);
246
- $this->_OldLink = get_permalink($postID);
247
- $this->_OldTmpl = $this->GetPostTemplate($this->_OldPost);
248
- $this->_OldCats = $this->GetPostCategories($this->_OldPost);
249
- $this->_old_tags = $this->get_post_tags( $this->_OldPost );
250
- $this->_OldStky = in_array($postID, get_option('sticky_posts'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  }
252
  }
253
 
254
  /**
255
  * Get the template path.
256
- * @param stdClass $post the post
257
- * @return string full path to file
 
258
  */
259
- protected function GetPostTemplate($post)
260
- {
261
  $id = $post->ID;
262
- $template = get_page_template_slug($id);
263
  $pagename = $post->post_name;
264
 
265
  $templates = array();
266
- if ($template && 0 === validate_file($template)) {
267
  $templates[] = $template;
268
  }
269
- if ($pagename) {
270
  $templates[] = "page-$pagename.php";
271
  }
272
- if ($id) {
273
  $templates[] = "page-$id.php";
274
  }
275
  $templates[] = 'page.php';
276
 
277
- return get_query_template('page', $templates);
278
  }
279
 
280
  /**
281
  * Get post categories (array of category names).
282
- * @param stdClass $post the post
283
- * @return array list of categories
 
284
  */
285
- protected function GetPostCategories($post)
286
- {
287
- return wp_get_post_categories($post->ID, array('fields' => 'names'));
 
 
 
288
  }
289
 
290
  /**
291
  * Get post tags (array of tag names).
292
  *
293
  * @param stdClass $post - The post.
294
- * @return array list of categories
295
  */
296
  protected function get_post_tags( $post ) {
297
- return wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) );
 
 
 
 
298
  }
299
 
300
  /**
301
  * Check all the post changes.
302
- * @param string $newStatus new status
303
- * @param string $oldStatus old status
304
- * @param stdClass $post the post
 
305
  */
306
- public function EventPostChanged($newStatus, $oldStatus, $post)
307
- {
308
- // ignorable states
309
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
310
  return;
311
  }
312
- if (empty($post->post_type)) {
313
  return;
314
  }
315
- if ($post->post_type == 'revision') {
316
  return;
317
  }
318
 
319
- $original = isset($_POST['original_post_status']) ? $_POST['original_post_status'] : '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
 
321
- WSAL_Sensors_Request::SetVars(array(
322
- '$newStatus' => $newStatus,
323
- '$oldStatus' => $oldStatus,
324
- '$original' => $original,
325
- ));
326
- // run checks
327
- if ($this->_OldPost) {
328
- if ($this->CheckOtherSensors($this->_OldPost)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  return;
330
  }
331
- if ($oldStatus == 'auto-draft' || $original == 'auto-draft') {
332
- // Handle create post events
333
- $this->CheckPostCreation($this->_OldPost, $post);
334
  } else {
335
- // Handle update post events
336
  $changes = 0
337
- + $this->CheckAuthorChange($this->_OldPost, $post)
338
- + $this->CheckStatusChange($this->_OldPost, $post)
339
- + $this->CheckParentChange($this->_OldPost, $post)
340
- + $this->CheckStickyChange($this->_OldStky, isset($_REQUEST['sticky']), $post)
341
- + $this->CheckVisibilityChange($this->_OldPost, $post, $oldStatus, $newStatus)
342
- + $this->CheckTemplateChange($this->_OldTmpl, $this->GetPostTemplate($post), $post)
343
- + $this->CheckCategoriesChange($this->_OldCats, $this->GetPostCategories($post), $post);
344
- $this->check_tags_change( $this->_old_tags, $this->get_post_tags( $post ), $post );
345
-
346
- if (!$changes) {
347
- $changes = $this->CheckDateChange($this->_OldPost, $post);
348
- if (!$changes) {
349
- $changes = $this->CheckPermalinkChange($this->_OldLink, get_permalink($post->ID), $post);
350
- // Comments/Trackbacks and Pingbacks
351
- if (!$changes) {
352
- $changes = $this->CheckCommentsPings($this->_OldPost, $post);
353
- if (!$changes) {
354
- $changes = $this->CheckModificationChange($post->ID, $this->_OldPost, $post);
355
  }
356
  }
357
  }
@@ -362,49 +466,71 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
362
 
363
  /**
364
  * Check post creation.
 
365
  * @global array $_POST
366
- * @param stdClass $oldPost old post
367
- * @param stdClass $newPost new post
368
  */
369
- protected function CheckPostCreation($oldPost, $newPost)
370
- {
371
- $WPActions = array('editpost', 'heartbeat');
372
- if (isset($_POST['action']) && in_array($_POST['action'], $WPActions)) {
373
- if (!in_array($newPost->post_type, array('attachment', 'revision', 'nav_menu_item'))) {
 
 
 
 
 
 
 
 
 
 
 
 
374
  $event = 0;
375
  $is_scheduled = false;
376
- switch ($newPost->post_status) {
377
  case 'publish':
378
- $event = $this->GetEventTypeForPostType($newPost, 2001, 2005, 2030);
379
  break;
380
  case 'draft':
381
- $event = $this->GetEventTypeForPostType($newPost, 2000, 2004, 2029);
382
  break;
383
  case 'future':
384
- $event = $this->GetEventTypeForPostType($newPost, 2074, 2075, 2076);
385
  $is_scheduled = true;
386
  break;
387
  case 'pending':
388
  $event = 2073;
389
  break;
390
  }
391
- if ($event) {
392
- $editorLink = $this->GetEditorLink($newPost);
393
- if ($is_scheduled) {
394
- $this->plugin->alerts->Trigger($event, array(
395
- 'PostType' => $newPost->post_type,
396
- 'PostTitle' => $newPost->post_title,
397
- 'PublishingDate' => $newPost->post_date,
398
- $editorLink['name'] => $editorLink['value']
399
- ));
 
 
 
 
 
400
  } else {
401
- $this->plugin->alerts->Trigger($event, array(
402
- 'PostID' => $newPost->ID,
403
- 'PostType' => $newPost->post_type,
404
- 'PostTitle' => $newPost->post_title,
405
- 'PostUrl' => get_permalink($newPost->ID),
406
- $editorLink['name'] => $editorLink['value']
407
- ));
 
 
 
 
408
  }
409
  }
410
  }
@@ -413,121 +539,155 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
413
 
414
  /**
415
  * Post future publishing.
416
- * @param integer $post_id post ID
 
417
  */
418
- public function EventPublishFuture($post_id)
419
- {
420
- $post = get_post($post_id);
421
- $event = $this->GetEventTypeForPostType($post, 2001, 2005, 2030);
422
-
423
- if ($event) {
424
- $editorLink = $this->GetEditorLink($newPost);
425
- $this->plugin->alerts->Trigger($event, array(
426
- 'PostID' => $post->ID,
427
- 'PostType' => $post->post_type,
428
- 'PostTitle' => $post->post_title,
429
- 'PostUrl' => get_permalink($post->ID),
430
- $editorLink['name'] => $editorLink['value']
431
- ));
 
 
 
432
  }
433
  }
434
 
435
  /**
436
  * Post permanently deleted.
437
- * @param integer $post_id post ID
 
438
  */
439
- public function EventPostDeleted($post_id)
440
- {
441
- $post = get_post($post_id);
442
- if ($this->CheckOtherSensors($post)) {
 
 
 
 
 
 
 
 
 
443
  return;
444
  }
445
- $WPActions = array('delete');
446
- if (isset($_REQUEST['action']) && in_array($_REQUEST['action'], $WPActions)) {
447
- if (!in_array($post->post_type, array('attachment', 'revision', 'nav_menu_item'))) { // ignore attachments, revisions and menu items
448
- $event = $this->GetEventTypeForPostType($post, 2008, 2009, 2033);
449
- // check WordPress backend operations
450
- if ($this->CheckAutoDraft($event, $post->post_title)) {
451
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  }
453
- $editorLink = $this->GetEditorLink($post);
454
- $this->plugin->alerts->Trigger($event, array(
455
- 'PostID' => $post->ID,
456
- 'PostType' => $post->post_type,
457
- 'PostTitle' => $post->post_title,
458
- ));
459
  }
460
  }
461
  }
462
 
463
  /**
464
  * Post moved to the trash.
465
- * @param integer $post_id post ID
 
466
  */
467
- public function EventPostTrashed($post_id)
468
- {
469
- $post = get_post($post_id);
470
- if ($this->CheckOtherSensors($post)) {
471
  return;
472
  }
473
- $event = $this->GetEventTypeForPostType($post, 2012, 2013, 2034);
474
- $editorLink = $this->GetEditorLink($post);
475
- $this->plugin->alerts->Trigger($event, array(
476
- 'PostID' => $post->ID,
477
- 'PostType' => $post->post_type,
478
- 'PostTitle' => $post->post_title,
479
- 'PostUrl' => get_permalink($post->ID),
480
- $editorLink['name'] => $editorLink['value']
481
- ));
 
 
 
 
482
  }
483
 
484
  /**
485
  * Post restored from trash.
486
- * @param integer $post_id post ID
 
487
  */
488
- public function EventPostUntrashed($post_id)
489
- {
490
- $post = get_post($post_id);
491
- if ($this->CheckOtherSensors($post)) {
492
  return;
493
  }
494
- $event = $this->GetEventTypeForPostType($post, 2014, 2015, 2035);
495
- $editorLink = $this->GetEditorLink($post);
496
- $this->plugin->alerts->Trigger($event, array(
497
- 'PostID' => $post->ID,
498
- 'PostType' => $post->post_type,
499
- 'PostTitle' => $post->post_title,
500
- $editorLink['name'] => $editorLink['value']
501
- ));
 
 
 
 
502
  }
503
 
504
  /**
505
  * Post date changed.
506
- * @param stdClass $oldPost old post
507
- * @param stdClass $newPost new post
 
508
  */
509
- protected function CheckDateChange($oldpost, $newpost)
510
- {
511
- $from = strtotime($oldpost->post_date);
512
- $to = strtotime($newpost->post_date);
513
- if ($oldpost->post_status == 'draft') {
514
  return 0;
515
  }
516
- $pending = $this->CheckReviewPendingChange($oldpost, $newpost);
517
- if ($pending) {
518
  return 0;
519
  }
520
- if ($from != $to) {
521
- $event = $this->GetEventTypeForPostType($oldpost, 2027, 2028, 2041);
522
- $editorLink = $this->GetEditorLink($oldpost);
523
- $this->plugin->alerts->Trigger($event, array(
524
- 'PostID' => $oldpost->ID,
525
- 'PostType' => $oldpost->post_type,
526
- 'PostTitle' => $oldpost->post_title,
527
- 'OldDate' => $oldpost->post_date,
528
- 'NewDate' => $newpost->post_date,
529
- $editorLink['name'] => $editorLink['value']
530
- ));
 
 
 
 
531
  return 1;
532
  }
533
  return 0;
@@ -535,19 +695,23 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
535
 
536
  /**
537
  * Revision used.
538
- * @param stdClass $oldPost old post
539
- * @param stdClass $newPost new post
 
540
  */
541
- protected function CheckReviewPendingChange($oldpost, $newpost)
542
- {
543
- if ($oldpost->post_status == 'pending') {
544
- $editorLink = $this->GetEditorLink($oldpost);
545
- $this->plugin->alerts->Trigger(2072, array(
546
- 'PostID' => $oldpost->ID,
547
- 'PostType' => $oldpost->post_type,
548
- 'PostTitle' => $oldpost->post_title,
549
- $editorLink['name'] => $editorLink['value']
550
- ));
 
 
 
551
  return 1;
552
  }
553
  return 0;
@@ -555,26 +719,30 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
555
 
556
  /**
557
  * Categories changed.
558
- * @param array $oldCats old categories
559
- * @param array $newCats new categories
560
- * @param stdClass $post the post
 
561
  */
562
- protected function CheckCategoriesChange($oldCats, $newCats, $post)
563
- {
564
- $oldCats = implode(', ', $oldCats);
565
- $newCats = implode(', ', $newCats);
566
- if ($oldCats != $newCats) {
567
- $event = $this->GetEventTypeForPostType($post, 2016, 0, 2036);
568
- if ($event) {
569
- $editorLink = $this->GetEditorLink($post);
570
- $this->plugin->alerts->Trigger($event, array(
571
- 'PostID' => $post->ID,
572
- 'PostType' => $post->post_type,
573
- 'PostTitle' => $post->post_title,
574
- 'OldCategories' => $oldCats ? $oldCats : 'no categories',
575
- 'NewCategories' => $newCats ? $newCats : 'no categories',
576
- $editorLink['name'] => $editorLink['value']
577
- ));
 
 
 
578
  return 1;
579
  }
580
  }
@@ -607,14 +775,18 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
607
  $add_event = $this->GetEventTypeForPostType( $post, 2119, 0, 0 );
608
  if ( $add_event ) {
609
  $editor_link = $this->GetEditorLink( $post );
610
- $post_status = ( 'publish' === $post->post_status ) ? 'published' : $this->post_status;
611
- $this->plugin->alerts->Trigger( $add_event, array(
612
- 'PostID' => $post->ID,
613
- 'status' => $post_status,
614
- 'post_title' => $post->post_title,
615
- 'tag' => $added_tags ? $added_tags : 'no tags',
616
- $editor_link['name'] => $editor_link['value'],
617
- ) );
 
 
 
 
618
  }
619
  }
620
 
@@ -622,14 +794,18 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
622
  $remove_event = $this->GetEventTypeForPostType( $post, 2120, 0, 0 );
623
  if ( $remove_event ) {
624
  $editor_link = $this->GetEditorLink( $post );
625
- $post_status = ( 'publish' === $post->post_status ) ? 'published' : $this->post_status;
626
- $this->plugin->alerts->Trigger( $remove_event, array(
627
- 'PostID' => $post->ID,
628
- 'status' => $post_status,
629
- 'post_title' => $post->post_title,
630
- 'tag' => $removed_tags ? $removed_tags : 'no tags',
631
- $editor_link['name'] => $editor_link['value'],
632
- ) );
 
 
 
 
633
  }
634
  }
635
 
@@ -640,60 +816,85 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
640
 
641
  /**
642
  * Author changed.
643
- * @param stdClass $oldPost old post
644
- * @param stdClass $newPost new post
 
645
  */
646
- protected function CheckAuthorChange($oldpost, $newpost)
647
- {
648
- if ($oldpost->post_author != $newpost->post_author) {
649
- $event = $this->GetEventTypeForPostType($oldpost, 2019, 2020, 2038);
650
- $editorLink = $this->GetEditorLink($oldpost);
651
- $oldAuthor = get_userdata($oldpost->post_author);
652
- $oldAuthor = (is_object($oldAuthor)) ? $oldAuthor->user_login : 'N/A';
653
- $newAuthor = get_userdata($newpost->post_author);
654
- $newAuthor = (is_object($newAuthor)) ? $newAuthor->user_login : 'N/A';
655
- $this->plugin->alerts->Trigger($event, array(
656
- 'PostID' => $oldpost->ID,
657
- 'PostType' => $oldpost->post_type,
658
- 'PostTitle' => $oldpost->post_title,
659
- 'OldAuthor' => $oldAuthor,
660
- 'NewAuthor' => $newAuthor,
661
- $editorLink['name'] => $editorLink['value']
662
- ));
 
 
 
663
  return 1;
664
  }
665
  }
666
 
667
  /**
668
  * Status changed.
669
- * @param stdClass $oldPost old post
670
- * @param stdClass $newPost new post
 
671
  */
672
- protected function CheckStatusChange($oldpost, $newpost)
673
- {
674
- if ($oldpost->post_status != $newpost->post_status) {
675
- if (isset($_REQUEST['publish'])) {
676
- // special case (publishing a post)
677
- $event = $this->GetEventTypeForPostType($oldpost, 2001, 2005, 2030);
678
- $editorLink = $this->GetEditorLink($newpost);
679
- $this->plugin->alerts->Trigger($event, array(
680
- 'PostID' => $newpost->ID,
681
- 'PostType' => $newpost->post_type,
682
- 'PostTitle' => $newpost->post_title,
683
- 'PostUrl' => get_permalink($newpost->ID),
684
- $editorLink['name'] => $editorLink['value']
685
- ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  } else {
687
- $event = $this->GetEventTypeForPostType($oldpost, 2021, 2022, 2039);
688
- $editorLink = $this->GetEditorLink($oldpost);
689
- $this->plugin->alerts->Trigger($event, array(
690
- 'PostID' => $oldpost->ID,
691
- 'PostType' => $oldpost->post_type,
692
- 'PostTitle' => $oldpost->post_title,
693
- 'OldStatus' => $oldpost->post_status,
694
- 'NewStatus' => $newpost->post_status,
695
- $editorLink['name'] => $editorLink['value']
696
- ));
 
 
 
 
697
  }
698
  return 1;
699
  }
@@ -701,25 +902,29 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
701
 
702
  /**
703
  * Post parent changed.
704
- * @param stdClass $oldPost old post
705
- * @param stdClass $newPost new post
 
706
  */
707
- protected function CheckParentChange($oldpost, $newpost)
708
- {
709
- if ($oldpost->post_parent != $newpost->post_parent) {
710
- $event = $this->GetEventTypeForPostType($oldpost, 0, 2047, 0);
711
- if ($event) {
712
- $editorLink = $this->GetEditorLink($oldpost);
713
- $this->plugin->alerts->Trigger($event, array(
714
- 'PostID' => $oldpost->ID,
715
- 'PostType' => $oldpost->post_type,
716
- 'PostTitle' => $oldpost->post_title,
717
- 'OldParent' => $oldpost->post_parent,
718
- 'NewParent' => $newpost->post_parent,
719
- 'OldParentName' => $oldpost->post_parent ? get_the_title($oldpost->post_parent) : 'no parent',
720
- 'NewParentName' => $newpost->post_parent ? get_the_title($newpost->post_parent) : 'no parent',
721
- $editorLink['name'] => $editorLink['value']
722
- ));
 
 
 
723
  return 1;
724
  }
725
  }
@@ -727,23 +932,27 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
727
 
728
  /**
729
  * Permalink changed.
730
- * @param string $oldLink old permalink
731
- * @param string $newLink new permalink
732
- * @param stdClass $post the post
 
733
  */
734
- protected function CheckPermalinkChange($oldLink, $newLink, $post)
735
- {
736
- if ($oldLink != $newLink) {
737
- $event = $this->GetEventTypeForPostType($post, 2017, 2018, 2037);
738
- $editorLink = $this->GetEditorLink($post);
739
- $this->plugin->alerts->Trigger($event, array(
740
- 'PostID' => $post->ID,
741
- 'PostType' => $post->post_type,
742
- 'PostTitle' => $post->post_title,
743
- 'OldUrl' => $oldLink,
744
- 'NewUrl' => $newLink,
745
- $editorLink['name'] => $editorLink['value']
746
- ));
 
 
 
747
  return 1;
748
  }
749
  return 0;
@@ -751,73 +960,81 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
751
 
752
  /**
753
  * Post visibility changed.
754
- * @param stdClass $oldPost old post
755
- * @param stdClass $newPost new post
756
- * @param string $oldStatus old status
757
- * @param string $newStatus new status
 
758
  */
759
- protected function CheckVisibilityChange($oldpost, $newpost, $oldStatus, $newStatus)
760
- {
761
- if ($oldStatus == 'draft' || $newStatus == 'draft') {
762
  return;
763
  }
764
 
765
- $oldVisibility = '';
766
- $newVisibility = '';
767
-
768
- if ($oldpost->post_password) {
769
- $oldVisibility = __('Password Protected', 'wp-security-audit-log');
770
- } elseif ($oldStatus == 'publish') {
771
- $oldVisibility = __('Public', 'wp-security-audit-log');
772
- } elseif ($oldStatus == 'private') {
773
- $oldVisibility = __('Private', 'wp-security-audit-log');
774
- }
775
-
776
- if ($newpost->post_password) {
777
- $newVisibility = __('Password Protected', 'wp-security-audit-log');
778
- } elseif ($newStatus == 'publish') {
779
- $newVisibility = __('Public', 'wp-security-audit-log');
780
- } elseif ($newStatus == 'private') {
781
- $newVisibility = __('Private', 'wp-security-audit-log');
782
- }
783
-
784
- if ($oldVisibility && $newVisibility && ($oldVisibility != $newVisibility)) {
785
- $event = $this->GetEventTypeForPostType($oldpost, 2025, 2026, 2040);
786
- $editorLink = $this->GetEditorLink($oldpost);
787
- $this->plugin->alerts->Trigger($event, array(
788
- 'PostID' => $oldpost->ID,
789
- 'PostType' => $oldpost->post_type,
790
- 'PostTitle' => $oldpost->post_title,
791
- 'OldVisibility' => $oldVisibility,
792
- 'NewVisibility' => $newVisibility,
793
- $editorLink['name'] => $editorLink['value']
794
- ));
 
 
 
 
795
  return 1;
796
  }
797
  }
798
 
799
  /**
800
  * Post template changed.
801
- * @param string $oldTmpl old template path
802
- * @param string $newTmpl new template path
803
- * @param stdClass $post the post
 
804
  */
805
- protected function CheckTemplateChange($oldTmpl, $newTmpl, $post)
806
- {
807
- if ($oldTmpl != $newTmpl) {
808
- $event = $this->GetEventTypeForPostType($post, 0, 2048, 0);
809
- if ($event) {
810
- $editorLink = $this->GetEditorLink($post);
811
- $this->plugin->alerts->Trigger($event, array(
812
- 'PostID' => $post->ID,
813
- 'PostType' => $post->post_type,
814
- 'PostTitle' => $post->post_title,
815
- 'OldTemplate' => ucwords(str_replace(array('-' , '_'), ' ', basename($oldTmpl, '.php'))),
816
- 'NewTemplate' => ucwords(str_replace(array('-' , '_'), ' ', basename($newTmpl, '.php'))),
817
- 'OldTemplatePath' => $oldTmpl,
818
- 'NewTemplatePath' => $newTmpl,
819
- $editorLink['name'] => $editorLink['value']
820
- ));
 
 
 
821
  return 1;
822
  }
823
  }
@@ -825,69 +1042,77 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
825
 
826
  /**
827
  * Post sets as sticky changes.
828
- * @param string $oldTmpl old template path
829
- * @param string $newTmpl new template path
830
- * @param stdClass $post the post
 
831
  */
832
- protected function CheckStickyChange($oldStky, $newStky, $post)
833
- {
834
- if ($oldStky != $newStky) {
835
- $event = $newStky ? 2049 : 2050;
836
- $editorLink = $this->GetEditorLink($post);
837
- $this->plugin->alerts->Trigger($event, array(
838
- 'PostID' => $post->ID,
839
- 'PostType' => $post->post_type,
840
- 'PostTitle' => $post->post_title,
841
- 'PostUrl' => get_permalink($post->ID),
842
- $editorLink['name'] => $editorLink['value']
843
- ));
 
 
 
844
  return 1;
845
  }
846
  }
847
 
848
  /**
849
  * Post modified content.
850
- * @param integer $post_ID post ID
851
- * @param stdClass $oldPost old post
852
- * @param stdClass $newPost new post
 
853
  */
854
- public function CheckModificationChange($post_ID, $oldpost, $newpost)
855
- {
856
- if ($this->CheckOtherSensors($oldpost)) {
857
  return;
858
  }
859
- $changes = $this->CheckTitleChange($oldpost, $newpost);
860
- if (!$changes) {
861
- $contentChanged = $oldpost->post_content != $newpost->post_content; // TODO what about excerpts?
862
 
863
- if ($oldpost->post_modified != $newpost->post_modified) {
864
  $event = 0;
865
  // @see http://codex.wordpress.org/Class_Reference/WP_Query#Status_Parameters
866
- switch ($oldpost->post_status) { // TODO or should this be $newpost?
867
  case 'draft':
868
- if ($contentChanged) {
869
- $event = $this->GetEventTypeForPostType($newpost, 2068, 2069, 2070);
870
  } else {
871
- $event = $this->GetEventTypeForPostType($newpost, 2003, 2007, 2032);
872
  }
873
  break;
874
  case 'publish':
875
- if ($contentChanged) {
876
- $event = $this->GetEventTypeForPostType($newpost, 2065, 2066, 2067);
877
  } else {
878
- $event = $this->GetEventTypeForPostType($newpost, 2002, 2006, 2031);
879
  }
880
  break;
881
  }
882
- if ($event) {
883
- $editorLink = $this->GetEditorLink($oldpost);
884
- $this->plugin->alerts->Trigger($event, array(
885
- 'PostID' => $post_ID,
886
- 'PostType' => $oldpost->post_type,
887
- 'PostTitle' => $oldpost->post_title,
888
- 'PostUrl' => get_permalink($post_ID),
889
- $editorLink['name'] => $editorLink['value']
890
- ));
 
 
 
 
891
  return 1;
892
  }
893
  }
@@ -896,17 +1121,19 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
896
 
897
  /**
898
  * New category created.
899
- * @param integer $category_id category ID
 
900
  */
901
- public function EventCategoryCreation($category_id)
902
- {
903
- $category = get_category($category_id);
904
- $category_link = $this->getCategoryLink($category_id);
905
- $this->plugin->alerts->Trigger(2023, array(
906
- 'CategoryName' => $category->name,
907
- 'Slug' => $category->slug,
908
- 'CategoryLink' => $category_link
909
- ));
 
910
  }
911
 
912
  /**
@@ -917,49 +1144,75 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
917
  public function EventTagCreation( $tag_id ) {
918
  $tag = get_tag( $tag_id );
919
  $tag_link = $this->get_tag_link( $tag_id );
920
- $this->plugin->alerts->Trigger( 2121, array(
921
- 'TagName' => $tag->name,
922
- 'Slug' => $tag->slug,
923
- 'TagLink' => $tag_link,
924
- ) );
 
 
925
  }
926
 
927
  /**
928
  * Category deleted.
929
- * @global array $_POST post data
 
930
  */
931
- protected function CheckCategoryDeletion()
932
- {
933
- if (empty($_POST)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
934
  return;
935
  }
936
- $action = !empty($_POST['action']) ? $_POST['action']
937
- : (!empty($_POST['action2']) ? $_POST['action2'] : '');
938
- if (!$action) {
939
  return;
940
  }
941
 
942
- $categoryIds = array();
943
 
944
- if (isset($_POST['taxonomy'])) {
945
- if ($action == 'delete' && $_POST['taxonomy'] == 'category' && !empty($_POST['delete_tags'])) {
946
- // bulk delete
947
- foreach ( $_POST['delete_tags'] as $delete_tag ) {
948
- $categoryIds[] = $delete_tag;
 
 
 
949
  }
950
- } elseif ($action == 'delete-tag' && $_POST['taxonomy'] == 'category' && !empty($_POST['tag_ID'])) {
951
- // single delete
952
- $categoryIds[] = $_POST['tag_ID'];
 
 
 
953
  }
954
  }
955
 
956
- foreach ($categoryIds as $categoryID) {
957
- $category = get_category($categoryID);
958
- $this->plugin->alerts->Trigger(2024, array(
959
- 'CategoryID' => $categoryID,
960
- 'CategoryName' => $category->cat_name,
961
- 'Slug' => $category->slug
962
- ));
 
 
963
  }
964
  }
965
 
@@ -969,9 +1222,21 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
969
  * @global array $_POST - Post data
970
  */
971
  protected function check_tag_deletion() {
 
 
 
 
 
 
 
 
 
 
 
 
972
 
973
- // Filter global post array for security.
974
- $post_array = filter_input_array( INPUT_POST );
975
 
976
  // If post array is empty then return.
977
  if ( empty( $post_array ) ) {
@@ -1007,59 +1272,80 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1007
 
1008
  foreach ( $tag_ids as $tag_id ) {
1009
  $tag = get_tag( $tag_id );
1010
- $this->plugin->alerts->Trigger( 2122, array(
1011
- 'TagID' => $tag_id,
1012
- 'TagName' => $tag->name,
1013
- 'Slug' => $tag->slug,
1014
- ) );
 
 
1015
  }
1016
  }
1017
 
1018
  /**
1019
  * Changed the parent of the category.
1020
- * @global array $_POST post data
 
1021
  */
1022
- public function EventChangedCategoryParent()
1023
- {
1024
- if (empty($_POST)) {
 
 
 
 
 
 
 
 
 
 
1025
  return;
1026
  }
1027
- if (!current_user_can("manage_categories")) {
1028
  return;
1029
  }
1030
- if (isset($_POST['name']) && isset($_POST['tag_ID'])) {
1031
- $category = get_category($_POST['tag_ID']);
1032
- $category_link = $this->getCategoryLink($_POST['tag_ID']);
1033
- if ($category->parent != 0) {
1034
- $oldParent = get_category($category->parent);
1035
- $oldParentName = (empty($oldParent))? 'no parent' : $oldParent->name;
 
 
 
1036
  } else {
1037
- $oldParentName = 'no parent';
1038
  }
1039
- if (isset($_POST['parent'])) {
1040
- $newParent = get_category($_POST['parent']);
1041
- $newParentName = (empty($newParent))? 'no parent' : $newParent->name;
 
 
 
 
 
 
 
 
 
 
 
1042
  }
1043
- $this->plugin->alerts->Trigger(2052, array(
1044
- 'CategoryName' => $category->name,
1045
- 'OldParent' => $oldParentName,
1046
- 'NewParent' => $newParentName,
1047
- 'CategoryLink' => $category_link
1048
- ));
1049
  }
1050
  }
1051
 
1052
  /**
1053
  * Check auto draft and the setting: Hide Plugin in Plugins Page
1054
- * @param integer $code alert code
1055
- * @param string $title title
 
1056
  * @return boolean
1057
  */
1058
- private function CheckAutoDraft($code, $title)
1059
- {
1060
- if ($code == 2008 && $title == "auto-draft") {
1061
- // to do check setting else return false
1062
- if ($this->plugin->settings->IsWPBackend() == 1) {
1063
  return true;
1064
  } else {
1065
  return false;
@@ -1071,13 +1357,13 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1071
 
1072
  /**
1073
  * Builds revision link.
1074
- * @param integer $revision_id revision ID
1075
- * @return string|null link
 
1076
  */
1077
- private function getRevisionLink($revision_id)
1078
- {
1079
- if (!empty($revision_id)) {
1080
- return admin_url('revision.php?revision='.$revision_id);
1081
  } else {
1082
  return null;
1083
  }
@@ -1085,13 +1371,13 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1085
 
1086
  /**
1087
  * Builds category link.
1088
- * @param integer $category_id category ID
1089
- * @return string|null link
 
1090
  */
1091
- private function getCategoryLink($category_id)
1092
- {
1093
- if (!empty($category_id)) {
1094
- return admin_url('term.php?taxnomy=category&tag_ID='.$category_id);
1095
  } else {
1096
  return null;
1097
  }
@@ -1101,7 +1387,7 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1101
  * Builds tag link.
1102
  *
1103
  * @param integer $tag_id - Tag ID.
1104
- * @return string|null link
1105
  */
1106
  private function get_tag_link( $tag_id ) {
1107
  if ( ! empty( $tag_id ) ) {
@@ -1115,7 +1401,7 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1115
  * Ignore post from BBPress, WooCommerce Plugin
1116
  * Triggered on the Sensors
1117
  *
1118
- * @param stdClass $post the post.
1119
  */
1120
  private function CheckOtherSensors( $post ) {
1121
  if ( empty( $post ) || ! isset( $post->post_type ) ) {
@@ -1134,22 +1420,23 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1134
 
1135
  /**
1136
  * Triggered after save post for add revision link.
1137
- * @param integer $post_id post ID
1138
- * @param stdClass $post post
 
 
1139
  */
1140
- public function SetRevisionLink($post_id, $post, $update)
1141
- {
1142
- $revisions = wp_get_post_revisions($post_id);
1143
- if (!empty($revisions)) {
1144
- $revision = array_shift($revisions);
1145
-
1146
- $objOcc = new WSAL_Models_Occurrence();
1147
- $occ = $objOcc->GetByPostID($post_id);
1148
- $occ = count($occ) ? $occ[0] : null;
1149
- if (!empty($occ)) {
1150
- $revisionLink = $this->getRevisionLink($revision->ID);
1151
- if (!empty($revisionLink)) {
1152
- $occ->SetMetaValue('RevisionLink', $revisionLink);
1153
  }
1154
  }
1155
  }
@@ -1167,19 +1454,27 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1167
  return $post->post_title;
1168
  }
1169
 
1170
- $currentPath = $_SERVER['REQUEST_URI'];
1171
- if ( ! empty( $_SERVER['HTTP_REFERER'] )
1172
- && strpos( $_SERVER['HTTP_REFERER'], $currentPath ) !== false ) {
 
 
 
1173
  // Ignore this if we were on the same page so we avoid double audit entries.
1174
  return;
1175
  }
1176
  if ( ! empty( $post->post_title ) ) {
1177
  $event = $this->GetEventTypeForPostType( $post, 2101, 2103, 2105 );
1178
- $this->plugin->alerts->Trigger( $event, array(
1179
- 'PostType' => $post->post_type,
1180
- 'PostTitle' => $post->post_title,
1181
- 'PostUrl' => get_permalink( $post->ID ),
1182
- ) );
 
 
 
 
 
1183
  }
1184
  }
1185
  }
@@ -1187,30 +1482,39 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1187
 
1188
  /**
1189
  * Alerts for Editing of Posts, Pages and Custom Posts.
1190
- * @param stdClass $post post
 
1191
  */
1192
- public function EditingPost($post)
1193
- {
1194
- if (is_user_logged_in()) {
1195
- if (is_admin()) {
1196
- if ($this->CheckOtherSensors($post)) {
1197
  return $post;
1198
  }
1199
- $currentPath = $_SERVER["SCRIPT_NAME"] . "?post=" . $post->ID;
1200
- if (!empty($_SERVER["HTTP_REFERER"])
1201
- && strpos($_SERVER["HTTP_REFERER"], $currentPath) !== false) {
1202
- //Ignore this if we were on the same page so we avoid double audit entries
 
 
 
 
1203
  return $post;
1204
  }
1205
- if (!empty($post->post_title)) {
1206
- $event = $this->GetEventTypeForPostType($post, 2100, 2102, 2104);
1207
- if (!$this->WasTriggered($event)) {
1208
- $editorLink = $this->GetEditorLink($post);
1209
- $this->plugin->alerts->Trigger($event, array(
1210
- 'PostType' => $post->post_type,
1211
- 'PostTitle' => $post->post_title,
1212
- $editorLink['name'] => $editorLink['value']
1213
- ));
 
 
 
 
 
1214
  }
1215
  }
1216
  }
@@ -1220,17 +1524,17 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1220
 
1221
  /**
1222
  * Check if the alert was triggered.
1223
- * @param integer $alert_id alert code
 
1224
  * @return boolean
1225
  */
1226
- private function WasTriggered($alert_id)
1227
- {
1228
  $query = new WSAL_Models_OccurrenceQuery();
1229
- $query->addOrderBy("created_on", true);
1230
- $query->setLimit(1);
1231
- $lastOccurence = $query->getAdapter()->Execute($query);
1232
- if (!empty($lastOccurence)) {
1233
- if ($lastOccurence[0]->alert_id == $alert_id) {
1234
  return true;
1235
  }
1236
  }
@@ -1239,19 +1543,26 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1239
 
1240
  /**
1241
  * Changed title of a post.
1242
- * @param stdClass $oldPost old post
1243
- * @param stdClass $newPost new post
 
1244
  */
1245
- private function CheckTitleChange($oldpost, $newpost)
1246
- {
1247
- if ($oldpost->post_title != $newpost->post_title) {
1248
- $event = $this->GetEventTypeForPostType($newpost, 2086, 2087, 2088);
1249
- $editorLink = $this->GetEditorLink($oldpost);
1250
- $this->plugin->alerts->Trigger($event, array(
1251
- 'OldTitle' => $oldpost->post_title,
1252
- 'NewTitle' => $newpost->post_title,
1253
- $editorLink['name'] => $editorLink['value']
1254
- ));
 
 
 
 
 
 
1255
  return 1;
1256
  }
1257
  return 0;
@@ -1259,44 +1570,56 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1259
 
1260
  /**
1261
  * Comments/Trackbacks and Pingbacks check.
1262
- * @param stdClass $oldPost old post
1263
- * @param stdClass $newPost new post
 
1264
  */
1265
- private function CheckCommentsPings($oldpost, $newpost)
1266
- {
1267
  $result = 0;
1268
- // Comments
1269
- if ($oldpost->comment_status != $newpost->comment_status) {
1270
  $type = 'Comments';
1271
 
1272
- if ($newpost->comment_status == 'open') {
1273
- $event = $this->GetCommentsPingsEvent($newpost, 'enable');
1274
  } else {
1275
- $event = $this->GetCommentsPingsEvent($newpost, 'disable');
1276
  }
1277
 
1278
- $this->plugin->alerts->Trigger($event, array(
1279
- 'Type' => $type,
1280
- 'PostTitle' => $newpost->post_title,
1281
- 'PostUrl' => get_permalink($newpost->ID)
1282
- ));
 
 
 
 
 
 
1283
  $result = 1;
1284
  }
1285
- // Trackbacks and Pingbacks
1286
- if ($oldpost->ping_status != $newpost->ping_status) {
1287
  $type = 'Trackbacks and Pingbacks';
1288
 
1289
- if ($newpost->ping_status == 'open') {
1290
- $event = $this->GetCommentsPingsEvent($newpost, 'enable');
1291
  } else {
1292
- $event = $this->GetCommentsPingsEvent($newpost, 'disable');
1293
  }
1294
 
1295
- $this->plugin->alerts->Trigger($event, array(
1296
- 'Type' => $type,
1297
- 'PostTitle' => $newpost->post_title,
1298
- 'PostUrl' => get_permalink($newpost->ID)
1299
- ));
 
 
 
 
 
 
1300
  $result = 1;
1301
  }
1302
  return $result;
@@ -1304,34 +1627,34 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1304
 
1305
  /**
1306
  * Comments/Trackbacks and Pingbacks event code.
1307
- * @param stdClass $post the post
1308
- * @param string $status the status
 
1309
  */
1310
- private function GetCommentsPingsEvent($post, $status)
1311
- {
1312
- if ($post->post_type == 'post') {
1313
- if ($post->post_status == 'publish') {
1314
- if ($status == 'disable') {
1315
  $event = 2111;
1316
  } else {
1317
  $event = 2112;
1318
  }
1319
  } else {
1320
- if ($status == 'disable') {
1321
  $event = 2113;
1322
  } else {
1323
  $event = 2114;
1324
  }
1325
  }
1326
  } else {
1327
- if ($post->post_status == 'publish') {
1328
- if ($status == 'disable') {
1329
  $event = 2115;
1330
  } else {
1331
  $event = 2116;
1332
  }
1333
  } else {
1334
- if ($status == 'disable') {
1335
  $event = 2117;
1336
  } else {
1337
  $event = 2118;
@@ -1343,18 +1666,18 @@ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
1343
 
1344
  /**
1345
  * Get editor link.
1346
- * @param stdClass $post the post
1347
- * @return array $aLink name and value link
 
1348
  */
1349
- private function GetEditorLink($post)
1350
- {
1351
  $name = 'EditorLink';
1352
- $name .= ($post->post_type == 'page') ? 'Page' : 'Post' ;
1353
- $value = get_edit_post_link($post->ID);
1354
- $aLink = array(
1355
  'name' => $name,
1356
  'value' => $value,
1357
  );
1358
- return $aLink;
1359
  }
1360
  }
1
  <?php
2
  /**
3
+ * Sensor: Content
4
+ *
5
+ * Content sensor class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * WordPress contents (posts, pages and custom posts).
18
  *
19
  * 2000 User created a new blog post and saved it as draft
20
  * 2001 User published a blog post
96
  * 2123 User renamed tag
97
  * 2124 User changed tag slug
98
  * 2125 User changed tag description
99
+ *
100
+ * @package Wsal
101
+ * @subpackage Sensors
102
  */
103
  class WSAL_Sensors_Content extends WSAL_AbstractSensor {
104
+
105
  /**
106
+ * Old post.
107
+ *
108
+ * @var stdClass
109
  */
110
+ protected $_old_post = null;
111
 
112
  /**
113
+ * Old permalink.
114
+ *
115
+ * @var string
116
  */
117
+ protected $_old_link = null;
118
 
119
  /**
120
+ * Old categories.
121
+ *
122
+ * @var array
123
  */
124
+ protected $_old_cats = null;
125
 
126
  /**
127
  * Old tags.
131
  protected $_old_tags = null;
132
 
133
  /**
134
+ * Old path to file.
135
+ *
136
+ * @var string
137
  */
138
+ protected $_old_tmpl = null;
139
 
140
  /**
141
+ * Old post is marked as sticky.
142
+ *
143
+ * @var boolean
144
  */
145
+ protected $_old_stky = null;
146
 
147
  /**
148
  * Listening to events using WP hooks.
149
  */
150
+ public function HookEvents() {
151
+ if ( current_user_can( 'edit_posts' ) ) {
152
+ add_action( 'admin_init', array( $this, 'EventWordPressInit' ) );
153
+ }
154
+ add_action( 'transition_post_status', array( $this, 'EventPostChanged' ), 10, 3 );
155
+ add_action( 'delete_post', array( $this, 'EventPostDeleted' ), 10, 1 );
156
+ add_action( 'wp_trash_post', array( $this, 'EventPostTrashed' ), 10, 1 );
157
+ add_action( 'untrash_post', array( $this, 'EventPostUntrashed' ) );
158
+ add_action( 'edit_category', array( $this, 'EventChangedCategoryParent' ) );
159
+ add_action( 'save_post', array( $this, 'SetRevisionLink' ), 10, 3 );
160
+ add_action( 'publish_future_post', array( $this, 'EventPublishFuture' ), 10, 1 );
161
+
162
+ add_action( 'create_category', array( $this, 'EventCategoryCreation' ), 10, 1 );
 
163
  add_action( 'create_post_tag', array( $this, 'EventTagCreation' ), 10, 1 );
164
 
165
  add_action( 'wp_head', array( $this, 'ViewingPost' ), 10 );
166
+ add_filter( 'post_edit_form_tag', array( $this, 'EditingPost' ), 10, 1 );
167
 
168
  add_filter( 'wp_update_term_data', array( $this, 'event_terms_rename' ), 10, 4 );
169
  }
178
  * @since 2.6.9
179
  */
180
  public function event_terms_rename( $data, $term_id, $taxonomy, $args ) {
 
181
  // Check if the taxonomy is term.
182
  if ( 'post_tag' !== $taxonomy ) {
183
  return $data;
193
  $old_name = $term->name;
194
  $old_slug = $term->slug;
195
  $old_desc = $term->description;
196
+ $term_link = $this->get_tag_link( $term_id );
197
 
198
  // Update if both names are not same.
199
  if ( $old_name !== $new_name ) {
200
+ $this->plugin->alerts->Trigger(
201
+ 2123, array(
202
+ 'old_name' => $old_name,
203
+ 'new_name' => $new_name,
204
+ 'TagLink' => $term_link,
205
+ )
206
+ );
207
  }
208
 
209
  // Update if both slugs are not same.
210
  if ( $old_slug !== $new_slug ) {
211
+ $this->plugin->alerts->Trigger(
212
+ 2124, array(
213
+ 'tag' => $new_name,
214
+ 'old_slug' => $old_slug,
215
+ 'new_slug' => $new_slug,
216
+ 'TagLink' => $term_link,
217
+ )
218
+ );
219
  }
220
 
221
  // Update if both descriptions are not same.
222
  if ( $old_desc !== $new_desc ) {
223
+ $this->plugin->alerts->Trigger(
224
+ 2125, array(
225
+ 'tag' => $new_name,
226
+ 'TagLink' => $term_link,
227
+ )
228
+ );
229
  }
230
  return $data;
231
 
233
 
234
  /**
235
  * Gets the alert code based on the type of post.
236
+ *
237
+ * @param stdClass $post - The post.
238
+ * @param integer $type_post - Alert code type post.
239
+ * @param integer $type_page - Alert code type page.
240
+ * @param integer $type_custom - Alert code type custom.
241
+ * @return integer - Alert code.
242
  */
243
+ protected function GetEventTypeForPostType( $post, $type_post, $type_page, $type_custom ) {
244
+ switch ( $post->post_type ) {
 
245
  case 'page':
246
+ return $type_page;
247
  case 'post':
248
+ return $type_post;
249
  default:
250
+ return $type_custom;
251
  }
252
  }
253
 
254
  /**
255
  * Triggered when a user accesses the admin area.
256
  */
257
+ public function EventWordPressInit() {
258
+ // Load old data, if applicable.
 
259
  $this->RetrieveOldData();
260
+
261
+ // Check for category changes.
262
  $this->CheckCategoryDeletion();
263
 
264
  // Check for tag changes.
267
 
268
  /**
269
  * Retrieve Old data.
270
+ *
271
+ * @global mixed $_POST - Post data.
272
  */
273
+ protected function RetrieveOldData() {
274
+ // Set filter input args.
275
+ $filter_input_args = array(
276
+ 'post_ID' => FILTER_VALIDATE_INT,
277
+ '_wpnonce' => FILTER_SANITIZE_STRING,
278
+ 'action' => FILTER_SANITIZE_STRING,
279
+ );
280
+
281
+ // Filter $_POST array for security.
282
+ $post_array = filter_input_array( INPUT_POST, $filter_input_args );
283
+
284
+ if ( isset( $post_array['_wpnonce'] )
285
+ && isset( $post_array['post_ID'] )
286
+ && wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
287
+ if ( isset( $post_array ) && isset( $post_array['post_ID'] )
288
+ && ! ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
289
+ && ! ( isset( $post_array['action'] ) && 'autosave' == $post_array['action'] )
290
+ ) {
291
+ $post_id = intval( $post_array['post_ID'] );
292
+ $this->_old_post = get_post( $post_id );
293
+ $this->_old_link = get_permalink( $post_id );
294
+ $this->_old_tmpl = $this->GetPostTemplate( $this->_old_post );
295
+ $this->_old_cats = $this->GetPostCategories( $this->_old_post );
296
+ $this->_old_tags = $this->get_post_tags( $this->_old_post );
297
+ $this->_old_stky = in_array( $post_id, get_option( 'sticky_posts' ) );
298
+ }
299
+ } elseif ( isset( $post_array['post_ID'] ) && current_user_can( 'edit_post', $post_array['post_ID'] ) ) {
300
+ $post_id = intval( $post_array['post_ID'] );
301
+ $this->_old_post = get_post( $post_id );
302
+ $this->_old_link = get_permalink( $post_id );
303
+ $this->_old_tmpl = $this->GetPostTemplate( $this->_old_post );
304
+ $this->_old_cats = $this->GetPostCategories( $this->_old_post );
305
+ $this->_old_tags = $this->get_post_tags( $this->_old_post );
306
+ $this->_old_stky = in_array( $post_id, get_option( 'sticky_posts' ) );
307
  }
308
  }
309
 
310
  /**
311
  * Get the template path.
312
+ *
313
+ * @param stdClass $post - The post.
314
+ * @return string - Full path to file.
315
  */
316
+ protected function GetPostTemplate( $post ) {
 
317
  $id = $post->ID;
318
+ $template = get_page_template_slug( $id );
319
  $pagename = $post->post_name;
320
 
321
  $templates = array();
322
+ if ( $template && 0 === validate_file( $template ) ) {
323
  $templates[] = $template;
324
  }
325
+ if ( $pagename ) {
326
  $templates[] = "page-$pagename.php";
327
  }
328
+ if ( $id ) {
329
  $templates[] = "page-$id.php";
330
  }
331
  $templates[] = 'page.php';
332
 
333
+ return get_query_template( 'page', $templates );
334
  }
335
 
336
  /**
337
  * Get post categories (array of category names).
338
+ *
339
+ * @param stdClass $post - The post.
340
+ * @return array - List of categories.
341
  */
342
+ protected function GetPostCategories( $post ) {
343
+ return wp_get_post_categories(
344
+ $post->ID, array(
345
+ 'fields' => 'names',
346
+ )
347
+ );
348
  }
349
 
350
  /**
351
  * Get post tags (array of tag names).
352
  *
353
  * @param stdClass $post - The post.
354
+ * @return array - List of tags.
355
  */
356
  protected function get_post_tags( $post ) {
357
+ return wp_get_post_tags(
358
+ $post->ID, array(
359
+ 'fields' => 'names',
360
+ )
361
+ );
362
  }
363
 
364
  /**
365
  * Check all the post changes.
366
+ *
367
+ * @param string $new_status - New status.
368
+ * @param string $old_status - Old status.
369
+ * @param stdClass $post - The post.
370
  */
371
+ public function EventPostChanged( $new_status, $old_status, $post ) {
372
+ // Ignorable states.
373
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
 
374
  return;
375
  }
376
+ if ( empty( $post->post_type ) ) {
377
  return;
378
  }
379
+ if ( 'revision' == $post->post_type ) {
380
  return;
381
  }
382
 
383
+ // Set filter input args.
384
+ $filter_input_args = array(
385
+ 'post_ID' => FILTER_VALIDATE_INT,
386
+ '_wpnonce' => FILTER_SANITIZE_STRING,
387
+ 'original_post_status' => FILTER_SANITIZE_STRING,
388
+ 'sticky' => FILTER_SANITIZE_STRING,
389
+ 'action' => FILTER_SANITIZE_STRING,
390
+ '_inline_edit' => FILTER_SANITIZE_STRING,
391
+ );
392
+
393
+ // Filter $_POST array for security.
394
+ $post_array = filter_input_array( INPUT_POST, $filter_input_args );
395
+
396
+ // Verify nonce.
397
+ if ( isset( $post_array['_wpnonce'] )
398
+ && isset( $post_array['post_ID'] )
399
+ && wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
400
+ // Edit Post Screen.
401
+ $original = isset( $post_array['original_post_status'] ) ? $post_array['original_post_status'] : '';
402
+ $this->trigger_post_change_alerts( $old_status, $new_status, $post, $original, isset( $post_array['sticky'] ) );
403
+ } elseif ( isset( $post_array['_inline_edit'] )
404
+ && 'inline-save' === $post_array['action']
405
+ && wp_verify_nonce( $post_array['_inline_edit'], 'inlineeditnonce' ) ) {
406
+ // Quick Post Edit.
407
+ $original = isset( $post_array['original_post_status'] ) ? $post_array['original_post_status'] : '';
408
+ $this->trigger_post_change_alerts( $old_status, $new_status, $post, $original, isset( $post_array['sticky'] ) );
409
+ }
410
+ }
411
 
412
+ /**
413
+ * Method: Trigger Post Change Alerts.
414
+ *
415
+ * @param string $old_status - Old status.
416
+ * @param string $new_status - New status.
417
+ * @param stdClass $post - The post.
418
+ * @param string $original - Original Post Status.
419
+ * @param string $sticky - Sticky post.
420
+ * @since 1.0.0
421
+ */
422
+ public function trigger_post_change_alerts( $old_status, $new_status, $post, $original, $sticky ) {
423
+ WSAL_Sensors_Request::SetVars(
424
+ array(
425
+ '$new_status' => $new_status,
426
+ '$old_status' => $old_status,
427
+ '$original' => $original,
428
+ )
429
+ );
430
+ // Run checks.
431
+ if ( $this->_old_post ) {
432
+ if ( $this->CheckOtherSensors( $this->_old_post ) ) {
433
  return;
434
  }
435
+ if ( 'auto-draft' == $old_status || 'auto-draft' == $original ) {
436
+ // Handle create post events.
437
+ $this->CheckPostCreation( $this->_old_post, $post );
438
  } else {
439
+ // Handle update post events.
440
  $changes = 0
441
+ + $this->CheckAuthorChange( $this->_old_post, $post )
442
+ + $this->CheckStatusChange( $this->_old_post, $post )
443
+ + $this->CheckParentChange( $this->_old_post, $post )
444
+ + $this->CheckStickyChange( $this->_old_stky, $sticky, $post )
445
+ + $this->CheckVisibilityChange( $this->_old_post, $post, $old_status, $new_status )
446
+ + $this->CheckTemplateChange( $this->_old_tmpl, $this->GetPostTemplate( $post ), $post )
447
+ + $this->CheckCategoriesChange( $this->_old_cats, $this->GetPostCategories( $post ), $post )
448
+ + $this->check_tags_change( $this->_old_tags, $this->get_post_tags( $post ), $post );
449
+
450
+ if ( ! $changes ) {
451
+ $changes = $this->CheckDateChange( $this->_old_post, $post );
452
+ if ( ! $changes ) {
453
+ $changes = $this->CheckPermalinkChange( $this->_old_link, get_permalink( $post->ID ), $post );
454
+ // Comments/Trackbacks and Pingbacks.
455
+ if ( ! $changes ) {
456
+ $changes = $this->CheckCommentsPings( $this->_old_post, $post );
457
+ if ( ! $changes ) {
458
+ $changes = $this->CheckModificationChange( $post->ID, $this->_old_post, $post );
459
  }
460
  }
461
  }
466
 
467
  /**
468
  * Check post creation.
469
+ *
470
  * @global array $_POST
471
+ * @param stdClass $old_post - Old post.
472
+ * @param stdClass $new_post - New post.
473
  */
474
+ protected function CheckPostCreation( $old_post, $new_post ) {
475
+ // Set filter input args.
476
+ $filter_input_args = array(
477
+ 'action' => FILTER_SANITIZE_STRING,
478
+ );
479
+
480
+ // Filter $_POST array for security.
481
+ $post_array = filter_input_array( INPUT_POST, $filter_input_args );
482
+
483
+ /**
484
+ * Nonce is already verified at this point.
485
+ *
486
+ * @see $this->EventPostChanged();
487
+ */
488
+ $wp_actions = array( 'editpost', 'heartbeat' );
489
+ if ( isset( $post_array['action'] ) && in_array( $post_array['action'], $wp_actions ) ) {
490
+ if ( ! in_array( $new_post->post_type, array( 'attachment', 'revision', 'nav_menu_item' ) ) ) {
491
  $event = 0;
492
  $is_scheduled = false;
493
+ switch ( $new_post->post_status ) {
494
  case 'publish':
495
+ $event = $this->GetEventTypeForPostType( $new_post, 2001, 2005, 2030 );
496
  break;
497
  case 'draft':
498
+ $event = $this->GetEventTypeForPostType( $new_post, 2000, 2004, 2029 );
499
  break;
500
  case 'future':
501
+ $event = $this->GetEventTypeForPostType( $new_post, 2074, 2075, 2076 );
502
  $is_scheduled = true;
503
  break;
504
  case 'pending':
505
  $event = 2073;
506
  break;
507
  }
508
+ if ( $event ) {
509
+ $editor_link = $this->GetEditorLink( $new_post );
510
+ if ( $is_scheduled ) {
511
+ $this->plugin->alerts->Trigger(
512
+ $event, array(
513
+ 'PostID' => $new_post->ID,
514
+ 'PostType' => $new_post->post_type,
515
+ 'PostTitle' => $new_post->post_title,
516
+ 'PostStatus' => $new_post->post_status,
517
+ 'PostDate' => $new_post->post_date,
518
+ 'PublishingDate' => $new_post->post_date,
519
+ $editor_link['name'] => $editor_link['value'],
520
+ )
521
+ );
522
  } else {
523
+ $this->plugin->alerts->Trigger(
524
+ $event, array(
525
+ 'PostID' => $new_post->ID,
526
+ 'PostType' => $new_post->post_type,
527
+ 'PostTitle' => $new_post->post_title,
528
+ 'PostStatus' => $new_post->post_status,
529
+ 'PostDate' => $new_post->post_date,
530
+ 'PostUrl' => get_permalink( $new_post->ID ),
531
+ $editor_link['name'] => $editor_link['value'],
532
+ )
533
+ );
534
  }
535
  }
536
  }
539
 
540
  /**
541
  * Post future publishing.
542
+ *
543
+ * @param integer $post_id - Post ID.
544
  */
545
+ public function EventPublishFuture( $post_id ) {
546
+ $post = get_post( $post_id );
547
+ $event = $this->GetEventTypeForPostType( $post, 2001, 2005, 2030 );
548
+
549
+ if ( $event ) {
550
+ $editor_link = $this->GetEditorLink( $post );
551
+ $this->plugin->alerts->Trigger(
552
+ $event, array(
553
+ 'PostID' => $post->ID,
554
+ 'PostType' => $post->post_type,
555
+ 'PostTitle' => $post->post_title,
556
+ 'PostStatus' => $post->post_status,
557
+ 'PostDate' => $post->post_date,
558
+ 'PostUrl' => get_permalink( $post->ID ),
559
+ $editor_link['name'] => $editor_link['value'],
560
+ )
561
+ );
562
  }
563
  }
564
 
565
  /**
566
  * Post permanently deleted.
567
+ *
568
+ * @param integer $post_id - Post ID.
569
  */
570
+ public function EventPostDeleted( $post_id ) {
571
+ // Set filter input args.
572
+ $filter_input_args = array(
573
+ 'action' => FILTER_SANITIZE_STRING,
574
+ '_wpnonce' => FILTER_SANITIZE_STRING,
575
+ );
576
+
577
+ // Filter $_GET array for security.
578
+ $get_array = filter_input_array( INPUT_GET, $filter_input_args );
579
+
580
+ // Exclude CPTs from external plugins.
581
+ $post = get_post( $post_id );
582
+ if ( $this->CheckOtherSensors( $post ) ) {
583
  return;
584
  }
585
+
586
+ // Verify nonce.
587
+ if ( isset( $get_array['_wpnonce'] ) && wp_verify_nonce( $get_array['_wpnonce'], 'delete-post_' . $post_id ) ) {
588
+ $wp_actions = array( 'delete' );
589
+ if ( isset( $get_array['action'] ) && in_array( $get_array['action'], $wp_actions ) ) {
590
+ if ( ! in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item' ) ) ) { // Ignore attachments, revisions and menu items.
591
+ $event = $this->GetEventTypeForPostType( $post, 2008, 2009, 2033 );
592
+ // Check WordPress backend operations.
593
+ if ( $this->CheckAutoDraft( $event, $post->post_title ) ) {
594
+ return;
595
+ }
596
+ $editor_link = $this->GetEditorLink( $post );
597
+ $this->plugin->alerts->Trigger(
598
+ $event, array(
599
+ 'PostID' => $post->ID,
600
+ 'PostType' => $post->post_type,
601
+ 'PostTitle' => $post->post_title,
602
+ 'PostStatus' => $post->post_status,
603
+ 'PostDate' => $post->post_date,
604
+ )
605
+ );
606
  }
 
 
 
 
 
 
607
  }
608
  }
609
  }
610
 
611
  /**
612
  * Post moved to the trash.
613
+ *
614
+ * @param integer $post_id - Post ID.
615
  */
616
+ public function EventPostTrashed( $post_id ) {
617
+ $post = get_post( $post_id );
618
+ if ( $this->CheckOtherSensors( $post ) ) {
 
619
  return;
620
  }
621
+ $event = $this->GetEventTypeForPostType( $post, 2012, 2013, 2034 );
622
+ $editor_link = $this->GetEditorLink( $post );
623
+ $this->plugin->alerts->Trigger(
624
+ $event, array(
625
+ 'PostID' => $post->ID,
626
+ 'PostType' => $post->post_type,
627
+ 'PostTitle' => $post->post_title,
628
+ 'PostStatus' => $post->post_status,
629
+ 'PostDate' => $post->post_date,
630
+ 'PostUrl' => get_permalink( $post->ID ),
631
+ $editor_link['name'] => $editor_link['value'],
632
+ )
633
+ );
634
  }
635
 
636
  /**
637
  * Post restored from trash.
638
+ *
639
+ * @param integer $post_id - Post ID.
640
  */
641
+ public function EventPostUntrashed( $post_id ) {
642
+ $post = get_post( $post_id );
643
+ if ( $this->CheckOtherSensors( $post ) ) {
 
644
  return;
645
  }
646
+ $event = $this->GetEventTypeForPostType( $post, 2014, 2015, 2035 );
647
+ $editor_link = $this->GetEditorLink( $post );
648
+ $this->plugin->alerts->Trigger(
649
+ $event, array(
650
+ 'PostID' => $post->ID,
651
+ 'PostType' => $post->post_type,
652
+ 'PostTitle' => $post->post_title,
653
+ 'PostStatus' => $post->post_status,
654
+ 'PostDate' => $post->post_date,
655
+ $editor_link['name'] => $editor_link['value'],
656
+ )
657
+ );
658
  }
659
 
660
  /**
661
  * Post date changed.
662
+ *
663
+ * @param stdClass $oldpost - Old post.
664
+ * @param stdClass $newpost - New post.
665
  */
666
+ protected function CheckDateChange( $oldpost, $newpost ) {
667
+ $from = strtotime( $oldpost->post_date );
668
+ $to = strtotime( $newpost->post_date );
669
+ if ( 'draft' == $oldpost->post_status ) {
 
670
  return 0;
671
  }
672
+ $pending = $this->CheckReviewPendingChange( $oldpost, $newpost );
673
+ if ( $pending ) {
674
  return 0;
675
  }
676
+ if ( $from != $to ) {
677
+ $event = $this->GetEventTypeForPostType( $oldpost, 2027, 2028, 2041 );
678
+ $editor_link = $this->GetEditorLink( $oldpost );
679
+ $this->plugin->alerts->Trigger(
680
+ $event, array(
681
+ 'PostID' => $oldpost->ID,
682
+ 'PostType' => $oldpost->post_type,
683
+ 'PostTitle' => $oldpost->post_title,
684
+ 'PostStatus' => $oldpost->post_status,
685
+ 'PostDate' => $newpost->post_date,
686
+ 'OldDate' => $oldpost->post_date,
687
+ 'NewDate' => $newpost->post_date,
688
+ $editor_link['name'] => $editor_link['value'],
689
+ )
690
+ );
691
  return 1;
692
  }
693
  return 0;
695
 
696
  /**
697
  * Revision used.
698
+ *
699
+ * @param stdClass $oldpost - Old post.
700
+ * @param stdClass $newpost - New post.
701
  */
702
+ protected function CheckReviewPendingChange( $oldpost, $newpost ) {
703
+ if ( 'pending' == $oldpost->post_status ) {
704
+ $editor_link = $this->GetEditorLink( $oldpost );
705
+ $this->plugin->alerts->Trigger(
706
+ 2072, array(
707
+ 'PostID' => $oldpost->ID,
708
+ 'PostType' => $oldpost->post_type,
709
+ 'PostTitle' => $oldpost->post_title,
710
+ 'PostStatus' => $oldpost->post_status,
711
+ 'PostDate' => $oldpost->post_date,
712
+ $editor_link['name'] => $editor_link['value'],
713
+ )
714
+ );
715
  return 1;
716
  }
717
  return 0;
719
 
720
  /**
721
  * Categories changed.
722
+ *
723
+ * @param array $old_cats - Old categories.
724
+ * @param array $new_cats - New categories.
725
+ * @param stdClass $post - The post.
726
  */
727
+ protected function CheckCategoriesChange( $old_cats, $new_cats, $post ) {
728
+ $old_cats = implode( ', ', $old_cats );
729
+ $new_cats = implode( ', ', $new_cats );
730
+ if ( $old_cats != $new_cats ) {
731
+ $event = $this->GetEventTypeForPostType( $post, 2016, 0, 2036 );
732
+ if ( $event ) {
733
+ $editor_link = $this->GetEditorLink( $post );
734
+ $this->plugin->alerts->Trigger(
735
+ $event, array(
736
+ 'PostID' => $post->ID,
737
+ 'PostType' => $post->post_type,
738
+ 'PostTitle' => $post->post_title,
739
+ 'PostStatus' => $post->post_status,
740
+ 'PostDate' => $post->post_date,
741
+ 'OldCategories' => $old_cats ? $old_cats : 'no categories',
742
+ 'NewCategories' => $new_cats ? $new_cats : 'no categories',
743
+ $editor_link['name'] => $editor_link['value'],
744
+ )
745
+ );
746
  return 1;
747
  }
748
  }
775
  $add_event = $this->GetEventTypeForPostType( $post, 2119, 0, 0 );
776
  if ( $add_event ) {
777
  $editor_link = $this->GetEditorLink( $post );
778
+ $post_status = ( 'publish' === $post->post_status ) ? 'published' : $post->post_status;
779
+ $this->plugin->alerts->Trigger(
780
+ $add_event, array(
781
+ 'PostID' => $post->ID,
782
+ 'PostStatus' => $post_status,
783
+ 'PostTitle' => $post->post_title,
784
+ 'PostType' => $post->post_type,
785
+ 'PostDate' => $post->post_date,
786
+ 'tag' => $added_tags ? $added_tags : 'no tags',
787
+ $editor_link['name'] => $editor_link['value'],
788
+ )
789
+ );
790
  }
791
  }
792
 
794
  $remove_event = $this->GetEventTypeForPostType( $post, 2120, 0, 0 );
795
  if ( $remove_event ) {
796
  $editor_link = $this->GetEditorLink( $post );
797
+ $post_status = ( 'publish' === $post->post_status ) ? 'published' : $post->post_status;
798
+ $this->plugin->alerts->Trigger(
799
+ $remove_event, array(
800
+ 'PostID' => $post->ID,
801
+ 'PostStatus' => $post_status,
802
+ 'PostTitle' => $post->post_title,
803
+ 'PostType' => $post->post_type,
804
+ 'PostDate' => $post->post_date,
805
+ 'tag' => $removed_tags ? $removed_tags : 'no tags',
806
+ $editor_link['name'] => $editor_link['value'],
807
+ )
808
+ );
809
  }
810
  }
811
 
816
 
817
  /**
818
  * Author changed.
819
+ *
820
+ * @param stdClass $oldpost - Old post.
821
+ * @param stdClass $newpost - New post.
822
  */
823
+ protected function CheckAuthorChange( $oldpost, $newpost ) {
824
+ if ( $oldpost->post_author != $newpost->post_author ) {
825
+ $event = $this->GetEventTypeForPostType( $oldpost, 2019, 2020, 2038 );
826
+ $editor_link = $this->GetEditorLink( $oldpost );
827
+ $old_author = get_userdata( $oldpost->post_author );
828
+ $old_author = (is_object( $old_author )) ? $old_author->user_login : 'N/A';
829
+ $new_author = get_userdata( $newpost->post_author );
830
+ $new_author = (is_object( $new_author )) ? $new_author->user_login : 'N/A';
831
+ $this->plugin->alerts->Trigger(
832
+ $event, array(
833
+ 'PostID' => $oldpost->ID,
834
+ 'PostType' => $oldpost->post_type,
835
+ 'PostTitle' => $oldpost->post_title,
836
+ 'PostStatus' => $oldpost->post_status,
837
+ 'PostDate' => $oldpost->post_date,
838
+ 'OldAuthor' => $old_author,
839
+ 'NewAuthor' => $new_author,
840
+ $editor_link['name'] => $editor_link['value'],
841
+ )
842
+ );
843
  return 1;
844
  }
845
  }
846
 
847
  /**
848
  * Status changed.
849
+ *
850
+ * @param stdClass $oldpost - Old post.
851
+ * @param stdClass $newpost - New post.
852
  */
853
+ protected function CheckStatusChange( $oldpost, $newpost ) {
854
+ // Set filter input args.
855
+ $filter_input_args = array(
856
+ 'publish' => FILTER_SANITIZE_STRING,
857
+ );
858
+
859
+ // Filter $_POST array for security.
860
+ $post_array = filter_input_array( INPUT_POST, $filter_input_args );
861
+
862
+ /**
863
+ * Nonce is already verified at this point.
864
+ *
865
+ * @see $this->EventPostChanged();
866
+ */
867
+ if ( $oldpost->post_status != $newpost->post_status ) {
868
+ if ( isset( $post_array['publish'] ) ) {
869
+ // Special case (publishing a post).
870
+ $event = $this->GetEventTypeForPostType( $oldpost, 2001, 2005, 2030 );
871
+ $editor_link = $this->GetEditorLink( $newpost );
872
+ $this->plugin->alerts->Trigger(
873
+ $event, array(
874
+ 'PostID' => $newpost->ID,
875
+ 'PostType' => $newpost->post_type,
876
+ 'PostTitle' => $newpost->post_title,
877
+ 'PostStatus' => $newpost->post_status,
878
+ 'PostDate' => $newpost->post_date,
879
+ 'PostUrl' => get_permalink( $newpost->ID ),
880
+ $editor_link['name'] => $editor_link['value'],
881
+ )
882
+ );
883
  } else {
884
+ $event = $this->GetEventTypeForPostType( $oldpost, 2021, 2022, 2039 );
885
+ $editor_link = $this->GetEditorLink( $oldpost );
886
+ $this->plugin->alerts->Trigger(
887
+ $event, array(
888
+ 'PostID' => $oldpost->ID,
889
+ 'PostType' => $oldpost->post_type,
890
+ 'PostTitle' => $oldpost->post_title,
891
+ 'PostStatus' => $newpost->post_status,
892
+ 'PostDate' => $oldpost->post_date,
893
+ 'OldStatus' => $oldpost->post_status,
894
+ 'NewStatus' => $newpost->post_status,
895
+ $editor_link['name'] => $editor_link['value'],
896
+ )
897
+ );
898
  }
899
  return 1;
900
  }
902
 
903
  /**
904
  * Post parent changed.
905
+ *
906
+ * @param stdClass $oldpost - Old post.
907
+ * @param stdClass $newpost - New post.
908
  */
909
+ protected function CheckParentChange( $oldpost, $newpost ) {
910
+ if ( $oldpost->post_parent != $newpost->post_parent ) {
911
+ $event = $this->GetEventTypeForPostType( $oldpost, 0, 2047, 0 );
912
+ if ( $event ) {
913
+ $editor_link = $this->GetEditorLink( $oldpost );
914
+ $this->plugin->alerts->Trigger(
915
+ $event, array(
916
+ 'PostID' => $oldpost->ID,
917
+ 'PostType' => $oldpost->post_type,
918
+ 'PostTitle' => $oldpost->post_title,
919
+ 'PostStatus' => $oldpost->post_status,
920
+ 'PostDate' => $oldpost->post_date,
921
+ 'OldParent' => $oldpost->post_parent,
922
+ 'NewParent' => $newpost->post_parent,
923
+ 'OldParentName' => $oldpost->post_parent ? get_the_title( $oldpost->post_parent ) : 'no parent',
924
+ 'NewParentName' => $newpost->post_parent ? get_the_title( $newpost->post_parent ) : 'no parent',
925
+ $editor_link['name'] => $editor_link['value'],
926
+ )
927
+ );
928
  return 1;
929
  }
930
  }
932
 
933
  /**
934
  * Permalink changed.
935
+ *
936
+ * @param string $old_link - Old permalink.
937
+ * @param string $new_link - New permalink.
938
+ * @param stdClass $post - The post.
939
  */
940
+ protected function CheckPermalinkChange( $old_link, $new_link, $post ) {
941
+ if ( $old_link != $new_link ) {
942
+ $event = $this->GetEventTypeForPostType( $post, 2017, 2018, 2037 );
943
+ $editor_link = $this->GetEditorLink( $post );
944
+ $this->plugin->alerts->Trigger(
945
+ $event, array(
946
+ 'PostID' => $post->ID,
947
+ 'PostType' => $post->post_type,
948
+ 'PostTitle' => $post->post_title,
949
+ 'PostStatus' => $post->post_status,
950
+ 'PostDate' => $post->post_date,
951
+ 'OldUrl' => $old_link,
952
+ 'NewUrl' => $new_link,
953
+ $editor_link['name'] => $editor_link['value'],
954
+ )
955
+ );
956
  return 1;
957
  }
958
  return 0;
960
 
961
  /**
962
  * Post visibility changed.
963
+ *
964
+ * @param stdClass $oldpost - Old post.
965
+ * @param stdClass $newpost - New post.
966
+ * @param string $old_status - Old status.
967
+ * @param string $new_status - New status.
968
  */
969
+ protected function CheckVisibilityChange( $oldpost, $newpost, $old_status, $new_status ) {
970
+ if ( 'draft' == $old_status || 'draft' == $new_status ) {
 
971
  return;
972
  }
973
 
974
+ $old_visibility = '';
975
+ $new_visibility = '';
976
+
977
+ if ( $oldpost->post_password ) {
978
+ $old_visibility = __( 'Password Protected', 'wp-security-audit-log' );
979
+ } elseif ( 'publish' == $old_status ) {
980
+ $old_visibility = __( 'Public', 'wp-security-audit-log' );
981
+ } elseif ( 'private' == $old_status ) {
982
+ $old_visibility = __( 'Private', 'wp-security-audit-log' );
983
+ }
984
+
985
+ if ( $newpost->post_password ) {
986
+ $new_visibility = __( 'Password Protected', 'wp-security-audit-log' );
987
+ } elseif ( 'publish' == $new_status ) {
988
+ $new_visibility = __( 'Public', 'wp-security-audit-log' );
989
+ } elseif ( 'private' == $new_status ) {
990
+ $new_visibility = __( 'Private', 'wp-security-audit-log' );
991
+ }
992
+
993
+ if ( $old_visibility && $new_visibility && ($old_visibility != $new_visibility) ) {
994
+ $event = $this->GetEventTypeForPostType( $oldpost, 2025, 2026, 2040 );
995
+ $editor_link = $this->GetEditorLink( $oldpost );
996
+ $this->plugin->alerts->Trigger(
997
+ $event, array(
998
+ 'PostID' => $oldpost->ID,
999
+ 'PostType' => $oldpost->post_type,
1000
+ 'PostTitle' => $oldpost->post_title,
1001
+ 'PostStatus' => $newpost->post_status,
1002
+ 'PostDate' => $oldpost->post_date,
1003
+ 'OldVisibility' => $old_visibility,
1004
+ 'NewVisibility' => $new_visibility,
1005
+ $editor_link['name'] => $editor_link['value'],
1006
+ )
1007
+ );
1008
  return 1;
1009
  }
1010
  }
1011
 
1012
  /**
1013
  * Post template changed.
1014
+ *
1015
+ * @param string $old_tmpl - Old template path.
1016
+ * @param string $new_tmpl - New template path.
1017
+ * @param stdClass $post - The post.
1018
  */
1019
+ protected function CheckTemplateChange( $old_tmpl, $new_tmpl, $post ) {
1020
+ if ( $old_tmpl != $new_tmpl ) {
1021
+ $event = $this->GetEventTypeForPostType( $post, 0, 2048, 0 );
1022
+ if ( $event ) {
1023
+ $editor_link = $this->GetEditorLink( $post );
1024
+ $this->plugin->alerts->Trigger(
1025
+ $event, array(
1026
+ 'PostID' => $post->ID,
1027
+ 'PostType' => $post->post_type,
1028
+ 'PostTitle' => $post->post_title,
1029
+ 'PostStatus' => $post->post_status,
1030
+ 'PostDate' => $post->post_date,
1031
+ 'OldTemplate' => ucwords( str_replace( array( '-', '_' ), ' ', basename( $old_tmpl, '.php' ) ) ),
1032
+ 'NewTemplate' => ucwords( str_replace( array( '-', '_' ), ' ', basename( $new_tmpl, '.php' ) ) ),
1033
+ 'OldTemplatePath' => $old_tmpl,
1034
+ 'NewTemplatePath' => $new_tmpl,
1035
+ $editor_link['name'] => $editor_link['value'],
1036
+ )
1037
+ );
1038
  return 1;
1039
  }
1040
  }
1042
 
1043
  /**
1044
  * Post sets as sticky changes.
1045
+ *
1046
+ * @param string $old_stky - Old template path.
1047
+ * @param string $new_stky - New template path.
1048
+ * @param stdClass $post - The post.
1049
  */
1050
+ protected function CheckStickyChange( $old_stky, $new_stky, $post ) {
1051
+ if ( $old_stky != $new_stky ) {
1052
+ $event = $new_stky ? 2049 : 2050;
1053
+ $editor_link = $this->GetEditorLink( $post );
1054
+ $this->plugin->alerts->Trigger(
1055
+ $event, array(
1056
+ 'PostID' => $post->ID,
1057
+ 'PostType' => $post->post_type,
1058
+ 'PostTitle' => $post->post_title,
1059
+ 'PostStatus' => $post->post_status,
1060
+ 'PostDate' => $post->post_date,
1061
+ 'PostUrl' => get_permalink( $post->ID ),
1062
+ $editor_link['name'] => $editor_link['value'],
1063
+ )
1064
+ );
1065
  return 1;
1066
  }
1067
  }
1068
 
1069
  /**
1070
  * Post modified content.
1071
+ *
1072
+ * @param integer $post_id - Post ID.
1073
+ * @param stdClass $oldpost - Old post.
1074
+ * @param stdClass $newpost - New post.
1075
  */
1076
+ public function CheckModificationChange( $post_id, $oldpost, $newpost ) {
1077
+ if ( $this->CheckOtherSensors( $oldpost ) ) {
 
1078
  return;
1079
  }
1080
+ $changes = $this->CheckTitleChange( $oldpost, $newpost );
1081
+ if ( ! $changes ) {
1082
+ $content_changed = $oldpost->post_content != $newpost->post_content; // TODO what about excerpts?
1083
 
1084
+ if ( $oldpost->post_modified != $newpost->post_modified ) {
1085
  $event = 0;
1086
  // @see http://codex.wordpress.org/Class_Reference/WP_Query#Status_Parameters
1087
+ switch ( $oldpost->post_status ) { // TODO or should this be $newpost?
1088
  case 'draft':
1089
+ if ( $content_changed ) {
1090
+ $event = $this->GetEventTypeForPostType( $newpost, 2068, 2069, 2070 );
1091
  } else {
1092
+ $event = $this->GetEventTypeForPostType( $newpost, 2003, 2007, 2032 );
1093
  }
1094
  break;
1095
  case 'publish':
1096
+ if ( $content_changed ) {
1097
+ $event = $this->GetEventTypeForPostType( $newpost, 2065, 2066, 2067 );
1098
  } else {
1099
+ $event = $this->GetEventTypeForPostType( $newpost, 2002, 2006, 2031 );
1100
  }
1101
  break;
1102
  }
1103
+ if ( $event ) {
1104
+ $editor_link = $this->GetEditorLink( $oldpost );
1105
+ $this->plugin->alerts->Trigger(
1106
+ $event, array(
1107
+ 'PostID' => $post_id,
1108
+ 'PostType' => $oldpost->post_type,
1109
+ 'PostTitle' => $oldpost->post_title,
1110
+ 'PostStatus' => $oldpost->post_status,
1111
+ 'PostDate' => $oldpost->post_date,
1112
+ 'PostUrl' => get_permalink( $post_id ),
1113
+ $editor_link['name'] => $editor_link['value'],
1114
+ )
1115
+ );
1116
  return 1;
1117
  }
1118
  }
1121
 
1122
  /**
1123
  * New category created.
1124
+ *
1125
+ * @param integer $category_id - Category ID.
1126
  */
1127
+ public function EventCategoryCreation( $category_id ) {
1128
+ $category = get_category( $category_id );
1129
+ $category_link = $this->getCategoryLink( $category_id );
1130
+ $this->plugin->alerts->Trigger(
1131
+ 2023, array(
1132
+ 'CategoryName' => $category->name,
1133
+ 'Slug' => $category->slug,
1134
+ 'CategoryLink' => $category_link,
1135
+ )
1136
+ );
1137
  }
1138
 
1139
  /**
1144
  public function EventTagCreation( $tag_id ) {
1145
  $tag = get_tag( $tag_id );
1146
  $tag_link = $this->get_tag_link( $tag_id );
1147
+ $this->plugin->alerts->Trigger(
1148
+ 2121, array(
1149
+ 'TagName' => $tag->name,
1150
+ 'Slug' => $tag->slug,
1151
+ 'TagLink' => $tag_link,
1152
+ )
1153
+ );
1154
  }
1155
 
1156
  /**
1157
  * Category deleted.
1158
+ *
1159
+ * @global array $_POST - Post data.
1160
  */
1161
+ protected function CheckCategoryDeletion() {
1162
+ // Set filter input args.
1163
+ $filter_input_args = array(
1164
+ '_wpnonce' => FILTER_SANITIZE_STRING,
1165
+ 'action' => FILTER_SANITIZE_STRING,
1166
+ 'action2' => FILTER_SANITIZE_STRING,
1167
+ 'taxonomy' => FILTER_SANITIZE_STRING,
1168
+ 'delete_tags' => array(
1169
+ 'filter' => FILTER_SANITIZE_STRING,
1170
+ 'flags' => FILTER_REQUIRE_ARRAY,
1171
+ ),
1172
+ 'tag_ID' => FILTER_VALIDATE_INT,
1173
+ );
1174
+
1175
+ // Filter $_POST array for security.
1176
+ $post_array = filter_input_array( INPUT_POST, $filter_input_args );
1177
+
1178
+ if ( empty( $post_array ) ) {
1179
  return;
1180
  }
1181
+ $action = ! empty( $post_array['action'] ) ? $post_array['action']
1182
+ : ( ! empty( $post_array['action2'] ) ? $post_array['action2'] : '');
1183
+ if ( ! $action ) {
1184
  return;
1185
  }
1186
 
1187
+ $category_ids = array();
1188
 
1189
+ if ( isset( $post_array['taxonomy'] ) ) {
1190
+ if ( 'delete' == $action
1191
+ && 'category' == $post_array['taxonomy']
1192
+ && ! empty( $post_array['delete_tags'] )
1193
+ && wp_verify_nonce( $post_array['_wpnonce'], 'bulk-tags' ) ) {
1194
+ // Bulk delete.
1195
+ foreach ( $post_array['delete_tags'] as $delete_tag ) {
1196
+ $category_ids[] = $delete_tag;
1197
  }
1198
+ } elseif ( 'delete-tag' == $action
1199
+ && 'category' == $post_array['taxonomy']
1200
+ && ! empty( $post_array['tag_ID'] )
1201
+ && wp_verify_nonce( $post_array['_wpnonce'], 'delete-tag_' . $post_array['tag_ID'] ) ) {
1202
+ // Single delete.
1203
+ $category_ids[] = $post_array['tag_ID'];
1204
  }
1205
  }
1206
 
1207
+ foreach ( $category_ids as $category_id ) {
1208
+ $category = get_category( $category_id );
1209
+ $this->plugin->alerts->Trigger(
1210
+ 2024, array(
1211
+ 'CategoryID' => $category_id,
1212
+ 'CategoryName' => $category->cat_name,
1213
+ 'Slug' => $category->slug,
1214
+ )
1215
+ );
1216
  }
1217
  }
1218
 
1222
  * @global array $_POST - Post data
1223
  */
1224
  protected function check_tag_deletion() {
1225
+ // Set filter input args.
1226
+ $filter_input_args = array(
1227
+ '_wpnonce' => FILTER_SANITIZE_STRING,
1228
+ 'action' => FILTER_SANITIZE_STRING,
1229
+ 'action2' => FILTER_SANITIZE_STRING,
1230
+ 'taxonomy' => FILTER_SANITIZE_STRING,
1231
+ 'delete_tags' => array(
1232
+ 'filter' => FILTER_SANITIZE_STRING,
1233
+ 'flags' => FILTER_REQUIRE_ARRAY,
1234
+ ),
1235
+ 'tag_ID' => FILTER_VALIDATE_INT,
1236
+ );
1237
 
1238
+ // Filter $_POST array for security.
1239
+ $post_array = filter_input_array( INPUT_POST, $filter_input_args );
1240
 
1241
  // If post array is empty then return.
1242
  if ( empty( $post_array ) ) {
1272
 
1273
  foreach ( $tag_ids as $tag_id ) {
1274
  $tag = get_tag( $tag_id );
1275
+ $this->plugin->alerts->Trigger(
1276
+ 2122, array(
1277
+ 'TagID' => $tag_id,
1278
+ 'TagName' => $tag->name,
1279
+ 'Slug' => $tag->slug,
1280
+ )
1281
+ );
1282
  }
1283
  }
1284
 
1285
  /**
1286
  * Changed the parent of the category.
1287
+ *
1288
+ * @global array $_POST - Post data.
1289
  */
1290
+ public function EventChangedCategoryParent() {
1291
+ // Set filter input args.
1292
+ $filter_input_args = array(
1293
+ '_wpnonce' => FILTER_SANITIZE_STRING,
1294
+ 'name' => FILTER_SANITIZE_STRING,
1295
+ 'parent' => FILTER_SANITIZE_STRING,
1296
+ 'tag_ID' => FILTER_VALIDATE_INT,
1297
+ );
1298
+
1299
+ // Filter $_POST array for security.
1300
+ $post_array = filter_input_array( INPUT_POST, $filter_input_args );
1301
+
1302
+ if ( empty( $post_array ) ) {
1303
  return;
1304
  }
1305
+ if ( ! current_user_can( 'manage_categories' ) ) {
1306
  return;
1307
  }
1308
+ if ( isset( $post_array['_wpnonce'] )
1309
+ && isset( $post_array['name'] )
1310
+ && isset( $post_array['tag_ID'] )
1311
+ && wp_verify_nonce( $post_array['_wpnonce'], 'update-tag_' . $post_array['tag_ID'] ) ) {
1312
+ $category = get_category( $post_array['tag_ID'] );
1313
+ $category_link = $this->getCategoryLink( $post_array['tag_ID'] );
1314
+ if ( 0 != $category->parent ) {
1315
+ $old_parent = get_category( $category->parent );
1316
+ $old_parent_name = (empty( $old_parent )) ? 'no parent' : $old_parent->name;
1317
  } else {
1318
+ $old_parent_name = 'no parent';
1319
  }
1320
+ if ( isset( $post_array['parent'] ) ) {
1321
+ $new_parent = get_category( $post_array['parent'] );
1322
+ $new_parent_name = (empty( $new_parent )) ? 'no parent' : $new_parent->name;
1323
+ }
1324
+
1325
+ if ( $old_parent_name !== $new_parent_name ) {
1326
+ $this->plugin->alerts->Trigger(
1327
+ 2052, array(
1328
+ 'CategoryName' => $category->name,
1329
+ 'OldParent' => $old_parent_name,
1330
+ 'NewParent' => $new_parent_name,
1331
+ 'CategoryLink' => $category_link,
1332
+ )
1333
+ );
1334
  }
 
 
 
 
 
 
1335
  }
1336
  }
1337
 
1338
  /**
1339
  * Check auto draft and the setting: Hide Plugin in Plugins Page
1340
+ *
1341
+ * @param integer $code - Alert code.
1342
+ * @param string $title - Title.
1343
  * @return boolean
1344
  */
1345
+ private function CheckAutoDraft( $code, $title ) {
1346
+ if ( 2008 == $code && 'auto-draft' == $title ) {
1347
+ // To do: Check setting else return false.
1348
+ if ( $this->plugin->settings->IsWPBackend() == 1 ) {
 
1349
  return true;
1350
  } else {
1351
  return false;
1357
 
1358
  /**
1359
  * Builds revision link.
1360
+ *
1361
+ * @param integer $revision_id - Revision ID.
1362
+ * @return string|null - Link.
1363
  */
1364
+ private function getRevisionLink( $revision_id ) {
1365
+ if ( ! empty( $revision_id ) ) {
1366
+ return admin_url( 'revision.php?revision=' . $revision_id );
 
1367
  } else {
1368
  return null;
1369
  }
1371
 
1372
  /**
1373
  * Builds category link.
1374
+ *
1375
+ * @param integer $category_id - Category ID.
1376
+ * @return string|null - Link.
1377
  */
1378
+ private function getCategoryLink( $category_id ) {
1379
+ if ( ! empty( $category_id ) ) {
1380
+ return admin_url( 'term.php?taxnomy=category&tag_ID=' . $category_id );
 
1381
  } else {
1382
  return null;
1383
  }
1387
  * Builds tag link.
1388
  *
1389
  * @param integer $tag_id - Tag ID.
1390
+ * @return string|null - Link.
1391
  */
1392
  private function get_tag_link( $tag_id ) {
1393
  if ( ! empty( $tag_id ) ) {
1401
  * Ignore post from BBPress, WooCommerce Plugin
1402
  * Triggered on the Sensors
1403
  *
1404
+ * @param stdClass $post - The post.
1405
  */
1406
  private function CheckOtherSensors( $post ) {
1407
  if ( empty( $post ) || ! isset( $post->post_type ) ) {
1420
 
1421
  /**
1422
  * Triggered after save post for add revision link.
1423
+ *
1424
+ * @param integer $post_id - Post ID.
1425
+ * @param stdClass $post - Post.
1426
+ * @param bool $update - True if update.
1427
  */
1428
+ public function SetRevisionLink( $post_id, $post, $update ) {
1429
+ $revisions = wp_get_post_revisions( $post_id );
1430
+ if ( ! empty( $revisions ) ) {
1431
+ $revision = array_shift( $revisions );
1432
+
1433
+ $obj_occ = new WSAL_Models_Occurrence();
1434
+ $occ = $obj_occ->GetByPostID( $post_id );
1435
+ $occ = count( $occ ) ? $occ[0] : null;
1436
+ if ( ! empty( $occ ) ) {
1437
+ $revision_link = $this->getRevisionLink( $revision->ID );
1438
+ if ( ! empty( $revision_link ) ) {
1439
+ $occ->SetMetaValue( 'RevisionLink', $revision_link );
 
1440
  }
1441
  }
1442
  }
1454
  return $post->post_title;
1455
  }
1456
 
1457
+ // Filter $_SERVER array for security.
1458
+ $server_array = filter_input_array( INPUT_SERVER );
1459
+
1460
+ $current_path = $server_array['REQUEST_URI'];
1461
+ if ( ! empty( $server_array['HTTP_REFERER'] )
1462
+ && strpos( $server_array['HTTP_REFERER'], $current_path ) !== false ) {
1463
  // Ignore this if we were on the same page so we avoid double audit entries.
1464
  return;
1465
  }
1466
  if ( ! empty( $post->post_title ) ) {
1467
  $event = $this->GetEventTypeForPostType( $post, 2101, 2103, 2105 );
1468
+ $this->plugin->alerts->Trigger(
1469
+ $event, array(
1470
+ 'PostID' => $post->ID,
1471
+ 'PostType' => $post->post_type,
1472
+ 'PostTitle' => $post->post_title,
1473
+ 'PostStatus' => $post->post_status,
1474
+ 'PostDate' => $post->post_date,
1475
+ 'PostUrl' => get_permalink( $post->ID ),
1476
+ )
1477
+ );
1478
  }
1479
  }
1480
  }
1482
 
1483
  /**
1484
  * Alerts for Editing of Posts, Pages and Custom Posts.
1485
+ *
1486
+ * @param stdClass $post - Post.
1487
  */
1488
+ public function EditingPost( $post ) {
1489
+ if ( is_user_logged_in() ) {
1490
+ if ( is_admin() ) {
1491
+ if ( $this->CheckOtherSensors( $post ) ) {
 
1492
  return $post;
1493
  }
1494
+
1495
+ // Filter $_SERVER array for security.
1496
+ $server_array = filter_input_array( INPUT_SERVER );
1497
+
1498
+ $current_path = $server_array['SCRIPT_NAME'] . '?post=' . $post->ID;
1499
+ if ( ! empty( $server_array['HTTP_REFERER'] )
1500
+ && strpos( $server_array['HTTP_REFERER'], $current_path ) !== false ) {
1501
+ // Ignore this if we were on the same page so we avoid double audit entries.
1502
  return $post;
1503
  }
1504
+ if ( ! empty( $post->post_title ) ) {
1505
+ $event = $this->GetEventTypeForPostType( $post, 2100, 2102, 2104 );
1506
+ if ( ! $this->WasTriggered( $event ) ) {
1507
+ $editor_link = $this->GetEditorLink( $post );
1508
+ $this->plugin->alerts->Trigger(
1509
+ $event, array(
1510
+ 'PostID' => $post->ID,
1511
+ 'PostType' => $post->post_type,
1512
+ 'PostTitle' => $post->post_title,
1513
+ 'PostStatus' => $post->post_status,
1514
+ 'PostDate' => $post->post_date,
1515
+ $editor_link['name'] => $editor_link['value'],
1516
+ )
1517
+ );
1518
  }
1519
  }
1520
  }
1524
 
1525
  /**
1526
  * Check if the alert was triggered.
1527
+ *
1528
+ * @param integer $alert_id - Alert code.
1529
  * @return boolean
1530
  */
1531
+ private function WasTriggered( $alert_id ) {
 
1532
  $query = new WSAL_Models_OccurrenceQuery();
1533
+ $query->addOrderBy( 'created_on', true );
1534
+ $query->setLimit( 1 );
1535
+ $last_occurence = $query->getAdapter()->Execute( $query );
1536
+ if ( ! empty( $last_occurence ) ) {
1537
+ if ( $last_occurence[0]->alert_id == $alert_id ) {
1538
  return true;
1539
  }
1540
  }
1543
 
1544
  /**
1545
  * Changed title of a post.
1546
+ *
1547
+ * @param stdClass $oldpost - Old post.
1548
+ * @param stdClass $newpost - New post.
1549
  */
1550
+ private function CheckTitleChange( $oldpost, $newpost ) {
1551
+ if ( $oldpost->post_title != $newpost->post_title ) {
1552
+ $event = $this->GetEventTypeForPostType( $newpost, 2086, 2087, 2088 );
1553
+ $editor_link = $this->GetEditorLink( $oldpost );
1554
+ $this->plugin->alerts->Trigger(
1555
+ $event, array(
1556
+ 'PostID' => $oldpost->ID,
1557
+ 'PostTitle' => $newpost->post_title,
1558
+ 'PostType' => $oldpost->post_type,
1559
+ 'PostStatus' => $oldpost->post_status,
1560
+ 'PostDate' => $oldpost->post_date,
1561
+ 'OldTitle' => $oldpost->post_title,
1562
+ 'NewTitle' => $newpost->post_title,
1563
+ $editor_link['name'] => $editor_link['value'],
1564
+ )
1565
+ );
1566
  return 1;
1567
  }
1568
  return 0;
1570
 
1571
  /**
1572
  * Comments/Trackbacks and Pingbacks check.
1573
+ *
1574
+ * @param stdClass $oldpost - Old post.
1575
+ * @param stdClass $newpost - New post.
1576
  */
1577
+ private function CheckCommentsPings( $oldpost, $newpost ) {
 
1578
  $result = 0;
1579
+ // Comments.
1580
+ if ( $oldpost->comment_status != $newpost->comment_status ) {
1581
  $type = 'Comments';
1582
 
1583
+ if ( 'open' == $newpost->comment_status ) {
1584
+ $event = $this->GetCommentsPingsEvent( $newpost, 'enable' );
1585
  } else {
1586
+ $event = $this->GetCommentsPingsEvent( $newpost, 'disable' );
1587
  }
1588
 
1589
+ $this->plugin->alerts->Trigger(
1590
+ $event, array(
1591
+ 'Type' => $type,
1592
+ 'PostID' => $newpost->ID,
1593
+ 'PostType' => $newpost->post_type,
1594
+ 'PostStatus' => $newpost->post_status,
1595
+ 'PostDate' => $newpost->post_date,
1596
+ 'PostTitle' => $newpost->post_title,
1597
+ 'PostUrl' => get_permalink( $newpost->ID ),
1598
+ )
1599
+ );
1600
  $result = 1;
1601
  }
1602
+ // Trackbacks and Pingbacks.
1603
+ if ( $oldpost->ping_status != $newpost->ping_status ) {
1604
  $type = 'Trackbacks and Pingbacks';
1605
 
1606
+ if ( 'open' == $newpost->ping_status ) {
1607
+ $event = $this->GetCommentsPingsEvent( $newpost, 'enable' );
1608
  } else {
1609
+ $event = $this->GetCommentsPingsEvent( $newpost, 'disable' );
1610
  }
1611
 
1612
+ $this->plugin->alerts->Trigger(
1613
+ $event, array(
1614
+ 'Type' => $type,
1615
+ 'PostID' => $newpost->ID,
1616
+ 'PostType' => $newpost->post_type,
1617
+ 'PostStatus' => $newpost->post_status,
1618
+ 'PostDate' => $newpost->post_date,
1619
+ 'PostTitle' => $newpost->post_title,
1620
+ 'PostUrl' => get_permalink( $newpost->ID ),
1621
+ )
1622
+ );
1623
  $result = 1;
1624
  }
1625
  return $result;
1627
 
1628
  /**
1629
  * Comments/Trackbacks and Pingbacks event code.
1630
+ *
1631
+ * @param stdClass $post - The post.
1632
+ * @param string $status - The status.
1633
  */
1634
+ private function GetCommentsPingsEvent( $post, $status ) {
1635
+ if ( 'post' == $post->post_type ) {
1636
+ if ( 'publish' == $post->post_status ) {
1637
+ if ( 'disable' == $status ) {
 
1638
  $event = 2111;
1639
  } else {
1640
  $event = 2112;
1641
  }
1642
  } else {
1643
+ if ( 'disable' == $status ) {
1644
  $event = 2113;
1645
  } else {
1646
  $event = 2114;
1647
  }
1648
  }
1649
  } else {
1650
+ if ( 'publish' == $post->post_status ) {
1651
+ if ( 'disable' == $status ) {
1652
  $event = 2115;
1653
  } else {
1654
  $event = 2116;
1655
  }
1656
  } else {
1657
+ if ( 'disable' == $status ) {
1658
  $event = 2117;
1659
  } else {
1660
  $event = 2118;
1666
 
1667
  /**
1668
  * Get editor link.
1669
+ *
1670
+ * @param stdClass $post - The post.
1671
+ * @return array $editor_link - Name and value link.
1672
  */
1673
+ private function GetEditorLink( $post ) {
 
1674
  $name = 'EditorLink';
1675
+ $name .= ( 'page' == $post->post_type ) ? 'Page' : 'Post' ;
1676
+ $value = get_edit_post_link( $post->ID );
1677
+ $editor_link = array(
1678
  'name' => $name,
1679
  'value' => $value,
1680
  );
1681
+ return $editor_link;
1682
  }
1683
  }
classes/Sensors/Database.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Database sensor.
6
  *
7
  * 5010 Plugin created tables
@@ -13,154 +25,185 @@
13
  * 5016 Unknown component created tables
14
  * 5017 Unknown component modified tables structure
15
  * 5018 Unknown component deleted tables
 
 
 
16
  */
17
- class WSAL_Sensors_Database extends WSAL_AbstractSensor
18
- {
19
- /**
20
- * Listening to events using WP hooks.
21
- */
22
- public function HookEvents()
23
- {
24
- add_action('dbdelta_queries', array($this, 'EventDBDeltaQuery'));
25
- add_action('query', array($this, 'EventDropQuery'));
26
- }
27
-
28
- /**
29
- * Checks for drop query.
30
- * @param WP_Query $query query object
31
- */
32
- public function EventDropQuery($query)
33
- {
34
- $table_names = array();
35
- $str = explode(" ", $query);
36
-
37
- if (preg_match("|DROP TABLE ([^ ]*)|", $query)) {
38
- if (!empty($str[4])) {
39
- array_push($table_names, $str[4]);
40
- } else {
41
- array_push($table_names, $str[2]);
42
- }
43
- $actype = basename($_SERVER['SCRIPT_NAME'], '.php');
44
- $alertOptions = $this->GetActionType($actype);
45
- }
46
-
47
- if (!empty($table_names)) {
48
- $event_code = $this->GetEventQueryType($actype, "delete");
49
- $alertOptions["TableNames"] = implode(",", $table_names);
50
- $this->plugin->alerts->Trigger($event_code, $alertOptions);
51
- }
52
- return $query;
53
- }
54
-
55
- /**
56
- * Checks DB Delta queries.
57
- * @param array $queries array of query
58
- */
59
- public function EventDBDeltaQuery($queries)
60
- {
61
- $typeQueries = array(
62
- "create" => array(),
63
- "update" => array(),
64
- "delete" => array()
65
- );
66
- global $wpdb;
67
-
68
- foreach ($queries as $qry) {
69
- $str = explode(" ", $qry);
70
- if (preg_match("|CREATE TABLE ([^ ]*)|", $qry)) {
71
- if ($wpdb->get_var("SHOW TABLES LIKE '" . $str[2] . "'") != $str[2]) {
72
- //some plugins keep trying to create tables even when they already exist- would result in too many alerts
73
- array_push($typeQueries['create'], $str[2]);
74
- }
75
- } else if (preg_match("|ALTER TABLE ([^ ]*)|", $qry)) {
76
- array_push($typeQueries['update'], $str[2]);
77
- } else if (preg_match("|DROP TABLE ([^ ]*)|", $qry)) {
78
- if (!empty($str[4])) {
79
- array_push($typeQueries['delete'], $str[4]);
80
- } else {
81
- array_push($typeQueries['delete'], $str[2]);
82
- }
83
- }
84
- }
85
-
86
- if (!empty($typeQueries["create"]) || !empty($typeQueries["update"]) || !empty($typeQueries["delete"])) {
87
- $actype = basename($_SERVER['SCRIPT_NAME'], '.php');
88
- $alertOptions = $this->GetActionType($actype);
89
-
90
- foreach ($typeQueries as $queryType => $tableNames) {
91
- if (!empty($tableNames)) {
92
- $event_code = $this->GetEventQueryType($actype, $queryType);
93
- $alertOptions["TableNames"] = implode(",", $tableNames);
94
- $this->plugin->alerts->Trigger($event_code, $alertOptions);
95
- }
96
- }
97
- }
98
-
99
- return $queries;
100
- }
101
-
102
- /**
103
- * Get code alert by action and type query.
104
- * @param string $type_action (plugins, themes or unknown)
105
- * @param string $type_query (create, update or delete)
106
- */
107
- protected function GetEventQueryType($type_action, $type_query)
108
- {
109
- switch ($type_action) {
110
- case 'plugins':
111
- if ($type_query == 'create') return 5010;
112
- else if ($type_query == 'update') return 5011;
113
- else if ($type_query == 'delete') return 5012;
114
- case 'themes':
115
- if ($type_query == 'create') return 5013;
116
- else if ($type_query == 'update') return 5014;
117
- else if ($type_query == 'delete') return 5015;
118
- default:
119
- if ($type_query == 'create') return 5016;
120
- else if ($type_query == 'update') return 5017;
121
- else if ($type_query == 'delete') return 5018;
122
- }
123
- }
124
-
125
- /**
126
- * Get info by action type.
127
- * @param string $actype (plugins, themes or unknown)
128
- */
129
- protected function GetActionType($actype)
130
- {
131
- $is_themes = $actype == 'themes';
132
- $is_plugins = $actype == 'plugins';
133
- //Action Plugin Component
134
- $alertOptions = array();
135
- if ($is_plugins) {
136
- if (isset($_REQUEST['plugin'])) {
137
- $pluginFile = $_REQUEST['plugin'];
138
- } else {
139
- $pluginFile = $_REQUEST['checked'][0];
140
- }
141
- $pluginName = basename($pluginFile, '.php');
142
- $pluginName = str_replace(array('_', '-', ' '), ' ', $pluginName);
143
- $pluginName = ucwords($pluginName);
144
- $alertOptions["Plugin"] = (object)array(
145
- 'Name' => $pluginName,
146
- );
147
- //Action Theme Component
148
- } else if ($is_themes) {
149
- if (isset($_REQUEST['theme'])) {
150
- $themeName = $_REQUEST['theme'];
151
- } else {
152
- $themeName = $_REQUEST['checked'][0];
153
- }
154
- $themeName = str_replace(array('_', '-', ' '), ' ', $themeName);
155
- $themeName = ucwords($themeName);
156
- $alertOptions["Theme"] = (object)array(
157
- 'Name' => $themeName,
158
- );
159
- //Action Unknown Component
160
- } else {
161
- $alertOptions["Component"] = "Unknown";
162
- }
163
-
164
- return $alertOptions;
165
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
1
  <?php
2
  /**
3
+ * Sensor: Database
4
+ *
5
+ * Database sensors class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Database sensor.
18
  *
19
  * 5010 Plugin created tables
25
  * 5016 Unknown component created tables
26
  * 5017 Unknown component modified tables structure
27
  * 5018 Unknown component deleted tables
28
+ *
29
+ * @package Wsal
30
+ * @subpackage Sensors
31
  */
32
+ class WSAL_Sensors_Database extends WSAL_AbstractSensor {
33
+
34
+ /**
35
+ * Listening to events using WP hooks.
36
+ */
37
+ public function HookEvents() {
38
+ add_action( 'dbdelta_queries', array( $this, 'EventDBDeltaQuery' ) );
39
+ add_action( 'query', array( $this, 'EventDropQuery' ) );
40
+ }
41
+
42
+ /**
43
+ * Checks for drop query.
44
+ *
45
+ * @param WP_Query $query - Query object.
46
+ */
47
+ public function EventDropQuery( $query ) {
48
+ $table_names = array();
49
+ $str = explode( ' ', $query );
50
+
51
+ if ( preg_match( '|DROP TABLE ([^ ]*)|', $query ) ) {
52
+ if ( ! empty( $str[4] ) ) {
53
+ array_push( $table_names, $str[4] );
54
+ } else {
55
+ array_push( $table_names, $str[2] );
56
+ }
57
+
58
+ // Filter $_SERVER array for security.
59
+ $server_array = filter_input_array( INPUT_SERVER );
60
+
61
+ $actype = basename( $server_array['SCRIPT_NAME'], '.php' );
62
+ $alert_options = $this->GetActionType( $actype );
63
+ }
64
+
65
+ if ( ! empty( $table_names ) ) {
66
+ $event_code = $this->GetEventQueryType( $actype, 'delete' );
67
+ $alert_options['TableNames'] = implode( ',', $table_names );
68
+ $this->plugin->alerts->Trigger( $event_code, $alert_options );
69
+ }
70
+ return $query;
71
+ }
72
+
73
+ /**
74
+ * Checks DB Delta queries.
75
+ *
76
+ * @param array $queries - Array of query.
77
+ */
78
+ public function EventDBDeltaQuery( $queries ) {
79
+ $type_queries = array(
80
+ 'create' => array(),
81
+ 'update' => array(),
82
+ 'delete' => array(),
83
+ );
84
+ global $wpdb;
85
+
86
+ foreach ( $queries as $qry ) {
87
+ $str = explode( ' ', $qry );
88
+ if ( preg_match( '|CREATE TABLE ([^ ]*)|', $qry ) ) {
89
+ if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $str[2] . "'" ) != $str[2] ) {
90
+ /**
91
+ * Some plugins keep trying to create tables even
92
+ * when they already exist- would result in too
93
+ * many alerts.
94
+ */
95
+ array_push( $type_queries['create'], $str[2] );
96
+ }
97
+ } elseif ( preg_match( '|ALTER TABLE ([^ ]*)|', $qry ) ) {
98
+ array_push( $type_queries['update'], $str[2] );
99
+ } elseif ( preg_match( '|DROP TABLE ([^ ]*)|', $qry ) ) {
100
+ if ( ! empty( $str[4] ) ) {
101
+ array_push( $type_queries['delete'], $str[4] );
102
+ } else {
103
+ array_push( $type_queries['delete'], $str[2] );
104
+ }
105
+ }
106
+ }
107
+
108
+ if ( ! empty( $type_queries['create'] ) || ! empty( $type_queries['update'] ) || ! empty( $type_queries['delete'] ) ) {
109
+ // Filter $_SERVER array for security.
110
+ $server_array = filter_input_array( INPUT_SERVER );
111
+
112
+ $actype = basename( $server_array['SCRIPT_NAME'], '.php' );
113
+ $alert_options = $this->GetActionType( $actype );
114
+
115
+ foreach ( $type_queries as $query_type => $table_names ) {
116
+ if ( ! empty( $table_names ) ) {
117
+ $event_code = $this->GetEventQueryType( $actype, $query_type );
118
+ $alert_options['TableNames'] = implode( ',', $table_names );
119
+ $this->plugin->alerts->Trigger( $event_code, $alert_options );
120
+ }
121
+ }
122
+ }
123
+
124
+ return $queries;
125
+ }
126
+
127
+ /**
128
+ * Get code alert by action and type query.
129
+ *
130
+ * @param string $type_action - Plugins, themes or unknown.
131
+ * @param string $type_query - Create, update or delete.
132
+ */
133
+ protected function GetEventQueryType( $type_action, $type_query ) {
134
+ switch ( $type_action ) {
135
+ case 'plugins':
136
+ if ( 'create' == $type_query ) {
137
+ return 5010;
138
+ } elseif ( 'update' == $type_query ) {
139
+ return 5011;
140
+ } elseif ( 'delete' == $type_query ) {
141
+ return 5012;
142
+ }
143
+ // In case of plugins.
144
+ case 'themes':
145
+ if ( 'create' == $type_query ) {
146
+ return 5013;
147
+ } elseif ( 'update' == $type_query ) {
148
+ return 5014;
149
+ } elseif ( 'delete' == $type_query ) {
150
+ return 5015;
151
+ }
152
+ // In case of themes.
153
+ default:
154
+ if ( 'create' == $type_query ) {
155
+ return 5016;
156
+ } elseif ( 'update' == $type_query ) {
157
+ return 5017;
158
+ } elseif ( 'delete' == $type_query ) {
159
+ return 5018;
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Get info by action type.
166
+ *
167
+ * @param string $actype - Plugins, themes or unknown.
168
+ */
169
+ protected function GetActionType( $actype ) {
170
+ // Filter $_GET array for security.
171
+ $get_array = filter_input_array( INPUT_GET );
172
+
173
+ $is_themes = 'themes' == $actype;
174
+ $is_plugins = 'plugins' == $actype;
175
+
176
+ // Action Plugin Component.
177
+ $alert_options = array();
178
+ if ( $is_plugins ) {
179
+ if ( isset( $get_array['plugin'] ) ) {
180
+ $plugin_file = $get_array['plugin'];
181
+ } else {
182
+ $plugin_file = $get_array['checked'][0];
183
+ }
184
+ $plugin_name = basename( $plugin_file, '.php' );
185
+ $plugin_name = str_replace( array( '_', '-', ' ' ), ' ', $plugin_name );
186
+ $plugin_name = ucwords( $plugin_name );
187
+ $alert_options['Plugin'] = (object) array(
188
+ 'Name' => $plugin_name,
189
+ );
190
+ // Action Theme Component.
191
+ } elseif ( $is_themes ) {
192
+ if ( isset( $get_array['theme'] ) ) {
193
+ $theme_name = $get_array['theme'];
194
+ } else {
195
+ $theme_name = $get_array['checked'][0];
196
+ }
197
+ $theme_name = str_replace( array( '_', '-', ' ' ), ' ', $theme_name );
198
+ $theme_name = ucwords( $theme_name );
199
+ $alert_options['Theme'] = (object) array(
200
+ 'Name' => $theme_name,
201
+ );
202
+ // Action Unknown Component.
203
+ } else {
204
+ $alert_options['Component'] = 'Unknown';
205
+ }
206
+
207
+ return $alert_options;
208
+ }
209
  }
classes/Sensors/Files.php CHANGED
@@ -1,87 +1,117 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Files sensor.
6
  *
7
- * 2010 User uploaded file from Uploads directory
8
  * 2011 User deleted file from Uploads directory
9
  * 2046 User changed a file using the theme editor
10
  * 2051 User changed a file using the plugin editor
 
 
 
11
  */
12
- class WSAL_Sensors_Files extends WSAL_AbstractSensor
13
- {
14
- /**
15
- * @var boolean file uploaded
16
- */
17
- protected $IsFileUploaded = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- /**
20
- * Listening to events using WP hooks.
21
- */
22
- public function HookEvents()
23
- {
24
- add_action('add_attachment', array($this, 'EventFileUploaded'));
25
- add_action('delete_attachment', array($this, 'EventFileUploadedDeleted'));
26
- add_action('admin_init', array($this, 'EventAdminInit'));
27
- }
28
-
29
- /**
30
- * File uploaded.
31
- * @param integer $attachmentID attachment ID
32
- */
33
- public function EventFileUploaded($attachmentID)
34
- {
35
- $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
36
- if ($action != 'upload-theme' && $action != 'upload-plugin') {
37
- $file = get_attached_file($attachmentID);
38
- $this->plugin->alerts->Trigger(2010, array(
39
- 'AttachmentID' => $attachmentID,
40
- 'FileName' => basename($file),
41
- 'FilePath' => dirname($file),
42
- ));
43
- }
44
- $this->IsFileUploaded = true;
45
- }
46
-
47
- /**
48
- * Deleted file from uploads directory.
49
- * @param integer $attachmentID attachment ID
50
- */
51
- public function EventFileUploadedDeleted($attachmentID)
52
- {
53
- if ($this->IsFileUploaded) {
54
- return;
55
- }
56
- $file = get_attached_file($attachmentID);
57
- $this->plugin->alerts->Trigger(2011, array(
58
- 'AttachmentID' => $attachmentID,
59
- 'FileName' => basename($file),
60
- 'FilePath' => dirname($file),
61
- ));
62
- }
63
-
64
- /**
65
- * Triggered when a user accesses the admin area.
66
- */
67
- public function EventAdminInit()
68
- {
69
- $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
70
- $is_theme_editor = basename($_SERVER['SCRIPT_NAME']) == 'theme-editor.php';
71
- $is_plugin_editor = basename($_SERVER['SCRIPT_NAME']) == 'plugin-editor.php';
72
-
73
- if ($is_theme_editor && $action == 'update') {
74
- $this->plugin->alerts->Trigger(2046, array(
75
- 'File' => $_REQUEST['file'],
76
- 'Theme' => $_REQUEST['theme'],
77
- ));
78
- }
79
-
80
- if ($is_plugin_editor && $action == 'update') {
81
- $this->plugin->alerts->Trigger(2051, array(
82
- 'File' => $_REQUEST['file'],
83
- 'Plugin' => $_REQUEST['plugin'],
84
- ));
85
- }
86
- }
87
  }
1
  <?php
2
  /**
3
+ * Sensor: Files
4
+ *
5
+ * Files sensors class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Files sensor.
18
  *
19
+ * 2010 User uploaded file in Uploads directory
20
  * 2011 User deleted file from Uploads directory
21
  * 2046 User changed a file using the theme editor
22
  * 2051 User changed a file using the plugin editor
23
+ *
24
+ * @package Wsal
25
+ * @subpackage Sensors
26
  */
27
+ class WSAL_Sensors_Files extends WSAL_AbstractSensor {
28
+
29
+ /**
30
+ * File uploaded.
31
+ *
32
+ * @var boolean
33
+ */
34
+ protected $is_file_uploaded = false;
35
+
36
+ /**
37
+ * Listening to events using WP hooks.
38
+ */
39
+ public function HookEvents() {
40
+ add_action( 'add_attachment', array( $this, 'EventFileUploaded' ) );
41
+ add_action( 'delete_attachment', array( $this, 'EventFileUploadedDeleted' ) );
42
+ add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
43
+ }
44
+
45
+ /**
46
+ * File uploaded.
47
+ *
48
+ * @param integer $attachment_id - Attachment ID.
49
+ */
50
+ public function EventFileUploaded( $attachment_id ) {
51
+ // Filter $_POST array for security.
52
+ $post_array = filter_input_array( INPUT_POST );
53
+
54
+ $action = isset( $post_array['action'] ) ? $post_array['action'] : '';
55
+ if ( 'upload-theme' !== $action && 'upload-plugin' !== $action ) {
56
+ $file = get_attached_file( $attachment_id );
57
+ $this->plugin->alerts->Trigger(
58
+ 2010, array(
59
+ 'AttachmentID' => $attachment_id,
60
+ 'FileName' => basename( $file ),
61
+ 'FilePath' => dirname( $file ),
62
+ )
63
+ );
64
+ }
65
+ $this->is_file_uploaded = true;
66
+ }
67
+
68
+ /**
69
+ * Deleted file from uploads directory.
70
+ *
71
+ * @param integer $attachment_id - Attachment ID.
72
+ */
73
+ public function EventFileUploadedDeleted( $attachment_id ) {
74
+ if ( $this->is_file_uploaded ) {
75
+ return;
76
+ }
77
+ $file = get_attached_file( $attachment_id );
78
+ $this->plugin->alerts->Trigger(
79
+ 2011, array(
80
+ 'AttachmentID' => $attachment_id,
81
+ 'FileName' => basename( $file ),
82
+ 'FilePath' => dirname( $file ),
83
+ )
84
+ );
85
+ }
86
+
87
+ /**
88
+ * Triggered when a user accesses the admin area.
89
+ */
90
+ public function EventAdminInit() {
91
+ // Filter global arrays for security.
92
+ $post_array = filter_input_array( INPUT_POST );
93
+ $server_array = filter_input_array( INPUT_SERVER );
94
+
95
+ $action = isset( $post_array['action'] ) ? $post_array['action'] : '';
96
+ $is_theme_editor = 'theme-editor.php' == basename( $server_array['SCRIPT_NAME'] );
97
+ $is_plugin_editor = 'plugin-editor.php' == basename( $server_array['SCRIPT_NAME'] );
98
+
99
+ if ( $is_theme_editor && 'update' === $action ) {
100
+ $this->plugin->alerts->Trigger(
101
+ 2046, array(
102
+ 'File' => $post_array['file'],
103
+ 'Theme' => $post_array['theme'],
104
+ )
105
+ );
106
+ }
107
 
108
+ if ( $is_plugin_editor && 'update' === $action ) {
109
+ $this->plugin->alerts->Trigger(
110
+ 2051, array(
111
+ 'File' => $post_array['file'],
112
+ 'Plugin' => $post_array['plugin'],
113
+ )
114
+ );
115
+ }
116
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  }
classes/Sensors/LogInOut.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Login/Logout sensor.
6
  *
7
  * 1000 User logged in
@@ -10,374 +22,425 @@
10
  * 1003 Login failed / non existing user
11
  * 1004 Login blocked
12
  * 4003 User has changed his or her password
 
 
 
13
  */
14
- class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor
15
- {
16
- /**
17
- * Transient name.
18
- * WordPress will prefix the name with "_transient_" or "_transient_timeout_" in the options table.
19
- */
20
- const TRANSIENT_FAILEDLOGINS = 'wsal-failedlogins-known';
21
- const TRANSIENT_FAILEDLOGINS_UNKNOWN = 'wsal-failedlogins-unknown';
22
-
23
- /**
24
- * @var WP_User current user object
25
- */
26
- protected $_current_user = null;
27
-
28
- /**
29
- * Listening to events using WP hooks.
30
- */
31
- public function HookEvents()
32
- {
33
- add_action('wp_login', array($this, 'EventLogin'), 10, 2);
34
- add_action('wp_logout', array($this, 'EventLogout'));
35
- add_action('password_reset', array($this, 'EventPasswordReset'), 10, 2);
36
- add_action('wp_login_failed', array($this, 'EventLoginFailure'));
37
- add_action('clear_auth_cookie', array($this, 'GetCurrentUser'), 10);
38
- add_filter('wp_login_blocked', array($this, 'EventLoginBlocked'), 10, 1);
39
-
40
- // Directory for logged in users log files.
41
- $user_upload_dir = wp_upload_dir();
42
- $user_upload_path = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/failed-logins/' );
43
- if ( ! $this->CheckDirectory( $user_upload_path ) ) {
44
- wp_mkdir_p( $user_upload_path );
45
- }
46
- }
47
-
48
- /**
49
- * Sets current user.
50
- */
51
- public function GetCurrentUser()
52
- {
53
- $this->_current_user = wp_get_current_user();
54
- }
55
-
56
- /**
57
- * Event Login.
58
- */
59
- public function EventLogin($user_login, $user = null)
60
- {
61
- if (empty($user)) {
62
- $user = get_user_by('login', $user_login);
63
- }
64
- $userRoles = $this->plugin->settings->GetCurrentUserRoles($user->roles);
65
- if ($this->plugin->settings->IsLoginSuperAdmin($user_login)) {
66
- $userRoles[] = 'superadmin';
67
- }
68
- $this->plugin->alerts->Trigger(1000, array(
69
- 'Username' => $user_login,
70
- 'CurrentUserRoles' => $userRoles,
71
- ), true);
72
- }
73
-
74
- /**
75
- * Event Logout.
76
- */
77
- public function EventLogout()
78
- {
79
- if ($this->_current_user->ID != 0) {
80
- $this->plugin->alerts->Trigger(1001, array(
81
- 'CurrentUserID' => $this->_current_user->ID,
82
- 'CurrentUserRoles' => $this->plugin->settings->GetCurrentUserRoles($this->_current_user->roles),
83
- ), true);
84
- }
85
- }
86
-
87
- /**
88
- * Login failure limit count.
89
- *
90
- * @return int
91
- */
92
- protected function GetLoginFailureLogLimit() {
93
- return $this->plugin->settings->get_failed_login_limit();
94
- }
95
-
96
- /**
97
- * Non-existing Login failure limit count.
98
- *
99
- * @return int
100
- */
101
- protected function GetVisitorLoginFailureLogLimit() {
102
- return $this->plugin->settings->get_visitor_failed_login_limit();
103
- }
104
-
105
- /**
106
- * Expiration of the transient saved in the WP database.
107
- * @return integer Time until expiration in seconds from now
108
- */
109
- protected function GetLoginFailureExpiration()
110
- {
111
- return 12 * 60 * 60;
112
- }
113
-
114
- /**
115
- * Check failure limit.
116
- * @param string $ip IP address
117
- * @param integer $site_id blog ID
118
- * @param WP_User $user user object
119
- * @return boolean passed limit true|false
120
- */
121
- protected function IsPastLoginFailureLimit($ip, $site_id, $user)
122
- {
123
- $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
124
- if ( $user ) {
125
- if ( -1 === (int) $this->GetLoginFailureLogLimit() ) {
126
- return false;
127
- } else {
128
- $dataKnown = $get_fn(self::TRANSIENT_FAILEDLOGINS);
129
- return ($dataKnown !== false) && isset($dataKnown[$site_id.":".$user->ID.":".$ip]) && ($dataKnown[$site_id.":".$user->ID.":".$ip] >= $this->GetLoginFailureLogLimit());
130
- }
131
- } else {
132
- if ( -1 === (int) $this->GetVisitorLoginFailureLogLimit() ) {
133
- return false;
134
- } else {
135
- $dataUnknown = $get_fn(self::TRANSIENT_FAILEDLOGINS_UNKNOWN);
136
- return ($dataUnknown !== false) && isset($dataUnknown[$site_id.":".$ip]) && ($dataUnknown[$site_id.":".$ip] >= $this->GetVisitorLoginFailureLogLimit());
137
- }
138
- }
139
- }
140
-
141
- /**
142
- * Increment failure limit.
143
- * @param string $ip IP address
144
- * @param integer $site_id blog ID
145
- * @param WP_User $user user object
146
- */
147
- protected function IncrementLoginFailure($ip, $site_id, $user)
148
- {
149
- $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
150
- $set_fn = $this->IsMultisite() ? 'set_site_transient' : 'set_transient';
151
- if ($user) {
152
- $dataKnown = $get_fn(self::TRANSIENT_FAILEDLOGINS);
153
- if (!$dataKnown) {
154
- $dataKnown = array();
155
- }
156
- if (!isset($dataKnown[$site_id.":".$user->ID.":".$ip])) {
157
- $dataKnown[$site_id.":".$user->ID.":".$ip] = 1;
158
- }
159
- $dataKnown[$site_id.":".$user->ID.":".$ip]++;
160
- $set_fn(self::TRANSIENT_FAILEDLOGINS, $dataKnown, $this->GetLoginFailureExpiration());
161
- } else {
162
- $dataUnknown = $get_fn(self::TRANSIENT_FAILEDLOGINS_UNKNOWN);
163
- if (!$dataUnknown) {
164
- $dataUnknown = array();
165
- }
166
- if (!isset($dataUnknown[$site_id.":".$ip])) {
167
- $dataUnknown[$site_id.":".$ip] = 1;
168
- }
169
- $dataUnknown[$site_id.":".$ip]++;
170
- $set_fn(self::TRANSIENT_FAILEDLOGINS_UNKNOWN, $dataUnknown, $this->GetLoginFailureExpiration());
171
- }
172
- }
173
-
174
- /**
175
- * Event Login failure.
176
- * @param string $username username
177
- */
178
- public function EventLoginFailure($username)
179
- {
180
- list($y, $m, $d) = explode('-', date('Y-m-d'));
181
-
182
- $ip = $this->plugin->settings->GetMainClientIP();
183
-
184
- $username = array_key_exists('log', $_POST) ? $_POST["log"] : $username;
185
- $newAlertCode = 1003;
186
- $user = get_user_by('login', $username);
187
- $site_id = (function_exists('get_current_blog_id') ? get_current_blog_id() : 0);
188
- if ($user) {
189
- $newAlertCode = 1002;
190
- $userRoles = $this->plugin->settings->GetCurrentUserRoles($user->roles);
191
- if ($this->plugin->settings->IsLoginSuperAdmin($username)) {
192
- $userRoles[] = 'superadmin';
193
- }
194
- }
195
-
196
- // Check if the alert is disabled from the "Enable/Disable Alerts" section
197
- if (!$this->plugin->alerts->IsEnabled($newAlertCode)) {
198
- return;
199
- }
200
-
201
- if ($this->IsPastLoginFailureLimit($ip, $site_id, $user)) {
202
- return;
203
- }
204
-
205
- $objOcc = new WSAL_Models_Occurrence();
206
-
207
- if ($newAlertCode == 1002) {
208
- if (!$this->plugin->alerts->CheckEnableUserRoles($username, $userRoles)) {
209
- return;
210
- }
211
- $occ = $objOcc->CheckKnownUsers(
212
- array(
213
- $ip,
214
- $username,
215
- 1002,
216
- $site_id,
217
- mktime(0, 0, 0, $m, $d, $y),
218
- mktime(0, 0, 0, $m, $d + 1, $y) - 1
219
- )
220
- );
221
- $occ = count($occ) ? $occ[0] : null;
222
-
223
- if (!empty($occ)) {
224
- // update existing record exists user
225
- $this->IncrementLoginFailure($ip, $site_id, $user);
226
- $new = $occ->GetMetaValue('Attempts', 0) + 1;
227
-
228
- if ( -1 !== (int) $this->GetLoginFailureLogLimit()
229
- && $new > $this->GetLoginFailureLogLimit() ) {
230
- $new = $this->GetLoginFailureLogLimit() . '+';
231
- }
232
-
233
- $occ->UpdateMetaValue('Attempts', $new);
234
- $occ->UpdateMetaValue('Username', $username);
235
-
236
- //$occ->SetMetaValue('CurrentUserRoles', $userRoles);
237
- $occ->created_on = null;
238
- $occ->Save();
239
- } else {
240
- // create a new record exists user
241
- $this->plugin->alerts->Trigger($newAlertCode, array(
242
- 'Attempts' => 1,
243
- 'Username' => $username,
244
- 'CurrentUserRoles' => $userRoles
245
- ));
246
- }
247
- } else {
248
- $occUnknown = $objOcc->CheckUnKnownUsers(
249
- array(
250
- $ip,
251
- 1003,
252
- $site_id,
253
- mktime(0, 0, 0, $m, $d, $y),
254
- mktime(0, 0, 0, $m, $d + 1, $y) - 1
255
- )
256
- );
257
-
258
- $occUnknown = count($occUnknown) ? $occUnknown[0] : null;
259
- if (!empty($occUnknown)) {
260
- // update existing record not exists user
261
- $this->IncrementLoginFailure($ip, $site_id, false);
262
- $new = $occUnknown->GetMetaValue('Attempts', 0) + 1;
263
-
264
- if ( 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
265
- $link_file = $this->WriteLog( $new, $username );
266
- }
267
-
268
- if ( -1 !== (int) $this->GetVisitorLoginFailureLogLimit()
269
- && $new > $this->GetVisitorLoginFailureLogLimit() ) {
270
- $new = $this->GetVisitorLoginFailureLogLimit() . '+';
271
- }
272
-
273
- $occUnknown->UpdateMetaValue('Attempts', $new);
274
- if ( ! empty( $link_file ) && 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
275
- $occUnknown->UpdateMetaValue( 'LogFileLink', $link_file );
276
- } else {
277
- $link_file = site_url() . '/wp-admin/admin.php?page=wsal-togglealerts#tab-users-profiles---activity';
278
- $occUnknown->UpdateMetaValue( 'LogFileLink', $link_file );
279
- }
280
- $occUnknown->created_on = null;
281
- $occUnknown->Save();
282
- } else {
283
- $link_file = site_url() . '/wp-admin/admin.php?page=wsal-togglealerts#tab-users-profiles---activity';
284
- $log_file_text = ' in a log file';
285
- if ( 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
286
- $link_file = $this->WriteLog( 1, $username );
287
- $log_file_text = ' with the usernames used during these failed login attempts';
288
- }
289
-
290
- // Create a new record not exists user.
291
- $this->plugin->alerts->Trigger( $newAlertCode, array(
292
- 'Attempts' => 1,
293
- 'LogFileLink' => $link_file,
294
- 'LogFileText' => $log_file_text,
295
- ) );
296
- }
297
- }
298
- }
299
-
300
- /**
301
- * Event changed password.
302
- * @param WP_User $user user object
303
- */
304
- public function EventPasswordReset($user, $new_pass)
305
- {
306
- if (!empty($user)) {
307
- $userRoles = $this->plugin->settings->GetCurrentUserRoles($user->roles);
308
- $this->plugin->alerts->Trigger(4003, array(
309
- 'Username' => $user->user_login,
310
- 'CurrentUserRoles' => $userRoles
311
- ), true);
312
- }
313
- }
314
-
315
- /**
316
- * Event login blocked.
317
- * @param string $username username
318
- */
319
- public function EventLoginBlocked($username)
320
- {
321
- $user = get_user_by('login', $username);
322
- $userRoles = $this->plugin->settings->GetCurrentUserRoles($user->roles);
323
-
324
- if ($this->plugin->settings->IsLoginSuperAdmin($username)) {
325
- $userRoles[] = 'superadmin';
326
- }
327
- $this->plugin->alerts->Trigger(1004, array(
328
- 'Username' => $username,
329
- 'CurrentUserRoles' => $userRoles
330
- ), true);
331
- }
332
-
333
- /**
334
- * Write log file.
335
- *
336
- * @param int $attempts - Number of attempt.
337
- * @param string $username - Username.
338
- * @author Ashar Irfan
339
- * @since 2.6.9
340
- */
341
- private function WriteLog( $attempts, $username = '' ) {
342
- $name_file = null;
343
-
344
- // Create/Append to the log file.
345
- $data = 'Attempts: ' . $attempts . ' — Username: ' . $username;
346
-
347
- $upload_dir = wp_upload_dir();
348
- $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/failed-logins/';
349
- $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/failed-logins/';
350
-
351
- // Check directory.
352
- if ( $this->CheckDirectory( $uploads_dir_path ) ) {
353
- $filename = 'failed_logins_usernames_' . date( 'Ymd' ) . '.log';
354
- $fp = $uploads_dir_path . $filename;
355
- $name_file = $uploads_url . $filename;
356
- if ( ! $file = fopen( $fp, 'a' ) ) {
357
- $i = 1;
358
- $file_opened = false;
359
- do {
360
- $fp2 = substr( $fp, 0, -4 ) . '_' . $i . '.log';
361
- if ( ! file_exists( $fp2 ) ) {
362
- if ( $file = fopen( $fp2, 'a' ) ) {
363
- $file_opened = true;
364
- $name_file = $uploads_url . substr( $name_file, 0, -4 ) . '_' . $i . '.log';
365
- }
366
- } else {
367
- $latest_filename = $this->GetLastModified( $uploads_dir_path, $filename );
368
- $fp_last = $uploads_dir_path . $latest_filename;
369
- if ( $file = fopen( $fp_last, 'a' ) ) {
370
- $file_opened = true;
371
- $name_file = $uploads_url . $latest_filename;
372
- }
373
- }
374
- $i++;
375
- } while ( ! $file_opened );
376
- }
377
- fwrite( $file, sprintf( "%s\n", $data ) );
378
- fclose( $file );
379
- }
380
-
381
- return $name_file;
382
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  }
1
  <?php
2
  /**
3
+ * Sensor: Log In & Log Out
4
+ *
5
+ * Log In & Out sensor class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Login/Logout sensor.
18
  *
19
  * 1000 User logged in
22
  * 1003 Login failed / non existing user
23
  * 1004 Login blocked
24
  * 4003 User has changed his or her password
25
+ *
26
+ * @package Wsal
27
+ * @subpackage Sensors
28
  */
29
+ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
30
+
31
+ /**
32
+ * Transient name.
33
+ * WordPress will prefix the name with "_transient_" or "_transient_timeout_" in the options table.
34
+ */
35
+ const TRANSIENT_FAILEDLOGINS = 'wsal-failedlogins-known';
36
+ const TRANSIENT_FAILEDLOGINS_UNKNOWN = 'wsal-failedlogins-unknown';
37
+
38
+ /**
39
+ * Current user object
40
+ *
41
+ * @var WP_User
42
+ */
43
+ protected $_current_user = null;
44
+
45
+ /**
46
+ * Listening to events using WP hooks.
47
+ */
48
+ public function HookEvents() {
49
+ add_action( 'wp_login', array( $this, 'EventLogin' ), 10, 2 );
50
+ add_action( 'wp_logout', array( $this, 'EventLogout' ) );
51
+ add_action( 'password_reset', array( $this, 'EventPasswordReset' ), 10, 2 );
52
+ add_action( 'wp_login_failed', array( $this, 'EventLoginFailure' ) );
53
+ add_action( 'clear_auth_cookie', array( $this, 'GetCurrentUser' ), 10 );
54
+ add_filter( 'wp_login_blocked', array( $this, 'EventLoginBlocked' ), 10, 1 );
55
+
56
+ // Directory for logged in users log files.
57
+ $user_upload_dir = wp_upload_dir();
58
+ $user_upload_path = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/failed-logins/' );
59
+ if ( ! $this->CheckDirectory( $user_upload_path ) ) {
60
+ wp_mkdir_p( $user_upload_path );
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Sets current user.
66
+ */
67
+ public function GetCurrentUser() {
68
+ $this->_current_user = wp_get_current_user();
69
+ }
70
+
71
+ /**
72
+ * Event Login.
73
+ *
74
+ * @param string $user_login - Username.
75
+ * @param object $user - WP_User object.
76
+ */
77
+ public function EventLogin( $user_login, $user = null ) {
78
+ if ( empty( $user ) ) {
79
+ $user = get_user_by( 'login', $user_login );
80
+ }
81
+ $user_roles = $this->plugin->settings->GetCurrentUserRoles( $user->roles );
82
+ if ( $this->plugin->settings->IsLoginSuperAdmin( $user_login ) ) {
83
+ $user_roles[] = 'superadmin';
84
+ }
85
+ $this->plugin->alerts->Trigger(
86
+ 1000, array(
87
+ 'Username' => $user_login,
88
+ 'CurrentUserRoles' => $user_roles,
89
+ ), true
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Event Logout.
95
+ */
96
+ public function EventLogout() {
97
+ if ( 0 != $this->_current_user->ID ) {
98
+ $this->plugin->alerts->Trigger(
99
+ 1001, array(
100
+ 'CurrentUserID' => $this->_current_user->ID,
101
+ 'CurrentUserRoles' => $this->plugin->settings->GetCurrentUserRoles( $this->_current_user->roles ),
102
+ ), true
103
+ );
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Login failure limit count.
109
+ *
110
+ * @return int
111
+ */
112
+ protected function GetLoginFailureLogLimit() {
113
+ return $this->plugin->settings->get_failed_login_limit();
114
+ }
115
+
116
+ /**
117
+ * Non-existing Login failure limit count.
118
+ *
119
+ * @return int
120
+ */
121
+ protected function GetVisitorLoginFailureLogLimit() {
122
+ return $this->plugin->settings->get_visitor_failed_login_limit();
123
+ }
124
+
125
+ /**
126
+ * Expiration of the transient saved in the WP database.
127
+ *
128
+ * @return integer Time until expiration in seconds from now
129
+ */
130
+ protected function GetLoginFailureExpiration() {
131
+ return 12 * 60 * 60;
132
+ }
133
+
134
+ /**
135
+ * Check failure limit.
136
+ *
137
+ * @param string $ip - IP address.
138
+ * @param integer $site_id - Blog ID.
139
+ * @param WP_User $user - User object.
140
+ * @return boolean - Passed limit true|false.
141
+ */
142
+ protected function IsPastLoginFailureLimit( $ip, $site_id, $user ) {
143
+ $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
144
+ if ( $user ) {
145
+ if ( -1 === (int) $this->GetLoginFailureLogLimit() ) {
146
+ return false;
147
+ } else {
148
+ $data_known = $get_fn( self::TRANSIENT_FAILEDLOGINS );
149
+ return ( false !== $data_known ) && isset( $data_known[ $site_id . ':' . $user->ID . ':' . $ip ] ) && ($data_known[ $site_id . ':' . $user->ID . ':' . $ip ] >= $this->GetLoginFailureLogLimit());
150
+ }
151
+ } else {
152
+ if ( -1 === (int) $this->GetVisitorLoginFailureLogLimit() ) {
153
+ return false;
154
+ } else {
155
+ $data_unknown = $get_fn( self::TRANSIENT_FAILEDLOGINS_UNKNOWN );
156
+ return ( false !== $data_unknown ) && isset( $data_unknown[ $site_id . ':' . $ip ] ) && ($data_unknown[ $site_id . ':' . $ip ] >= $this->GetVisitorLoginFailureLogLimit());
157
+ }
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Increment failure limit.
163
+ *
164
+ * @param string $ip - IP address.
165
+ * @param integer $site_id - Blog ID.
166
+ * @param WP_User $user - User object.
167
+ */
168
+ protected function IncrementLoginFailure( $ip, $site_id, $user ) {
169
+ $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
170
+ $set_fn = $this->IsMultisite() ? 'set_site_transient' : 'set_transient';
171
+ if ( $user ) {
172
+ $data_known = $get_fn( self::TRANSIENT_FAILEDLOGINS );
173
+ if ( ! $data_known ) {
174
+ $data_known = array();
175
+ }
176
+ if ( ! isset( $data_known[ $site_id . ':' . $user->ID . ':' . $ip ] ) ) {
177
+ $data_known[ $site_id . ':' . $user->ID . ':' . $ip ] = 1;
178
+ }
179
+ $data_known[ $site_id . ':' . $user->ID . ':' . $ip ]++;
180
+ $set_fn( self::TRANSIENT_FAILEDLOGINS, $data_known, $this->GetLoginFailureExpiration() );
181
+ } else {
182
+ $data_unknown = $get_fn( self::TRANSIENT_FAILEDLOGINS_UNKNOWN );
183
+ if ( ! $data_unknown ) {
184
+ $data_unknown = array();
185
+ }
186
+ if ( ! isset( $data_unknown[ $site_id . ':' . $ip ] ) ) {
187
+ $data_unknown[ $site_id . ':' . $ip ] = 1;
188
+ }
189
+ $data_unknown[ $site_id . ':' . $ip ]++;
190
+ $set_fn( self::TRANSIENT_FAILEDLOGINS_UNKNOWN, $data_unknown, $this->GetLoginFailureExpiration() );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Event Login failure.
196
+ *
197
+ * @param string $username Username.
198
+ */
199
+ public function EventLoginFailure( $username ) {
200
+ list($y, $m, $d) = explode( '-', date( 'Y-m-d' ) );
201
+
202
+ $ip = $this->plugin->settings->GetMainClientIP();
203
+
204
+ // Filter $_POST global array for security.
205
+ $post_array = filter_input_array( INPUT_POST );
206
+
207
+ $username = array_key_exists( 'log', $post_array ) ? $post_array['log'] : $username;
208
+ $username = sanitize_user( $username );
209
+ $new_alert_code = 1003;
210
+ $user = get_user_by( 'login', $username );
211
+ $site_id = (function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 0);
212
+ if ( $user ) {
213
+ $new_alert_code = 1002;
214
+ $user_roles = $this->plugin->settings->GetCurrentUserRoles( $user->roles );
215
+ if ( $this->plugin->settings->IsLoginSuperAdmin( $username ) ) {
216
+ $user_roles[] = 'superadmin';
217
+ }
218
+ }
219
+
220
+ // Check if the alert is disabled from the "Enable/Disable Alerts" section.
221
+ if ( ! $this->plugin->alerts->IsEnabled( $new_alert_code ) ) {
222
+ return;
223
+ }
224
+
225
+ if ( $this->IsPastLoginFailureLimit( $ip, $site_id, $user ) ) {
226
+ return;
227
+ }
228
+
229
+ $obj_occurrence = new WSAL_Models_Occurrence();
230
+
231
+ if ( 1002 == $new_alert_code ) {
232
+ if ( ! $this->plugin->alerts->CheckEnableUserRoles( $username, $user_roles ) ) {
233
+ return;
234
+ }
235
+ $occ = $obj_occurrence->CheckKnownUsers(
236
+ array(
237
+ $ip,
238
+ $username,
239
+ 1002,
240
+ $site_id,
241
+ mktime( 0, 0, 0, $m, $d, $y ),
242
+ mktime( 0, 0, 0, $m, $d + 1, $y ) - 1,
243
+ )
244
+ );
245
+ $occ = count( $occ ) ? $occ[0] : null;
246
+
247
+ if ( ! empty( $occ ) ) {
248
+ // Update existing record exists user.
249
+ $this->IncrementLoginFailure( $ip, $site_id, $user );
250
+ $new = $occ->GetMetaValue( 'Attempts', 0 ) + 1;
251
+
252
+ if ( -1 !== (int) $this->GetLoginFailureLogLimit()
253
+ && $new > $this->GetLoginFailureLogLimit() ) {
254
+ $new = $this->GetLoginFailureLogLimit() . '+';
255
+ }
256
+
257
+ $occ->UpdateMetaValue( 'Attempts', $new );
258
+ $occ->UpdateMetaValue( 'Username', $username );
259
+
260
+ // $occ->SetMetaValue('CurrentUserRoles', $user_roles);
261
+ $occ->created_on = null;
262
+ $occ->Save();
263
+ } else {
264
+ // Create a new record exists user.
265
+ $this->plugin->alerts->Trigger(
266
+ $new_alert_code, array(
267
+ 'Attempts' => 1,
268
+ 'Username' => $username,
269
+ 'CurrentUserRoles' => $user_roles,
270
+ )
271
+ );
272
+ }
273
+ } else {
274
+ $occ_unknown = $obj_occurrence->CheckUnKnownUsers(
275
+ array(
276
+ $ip,
277
+ 1003,
278
+ $site_id,
279
+ mktime( 0, 0, 0, $m, $d, $y ),
280
+ mktime( 0, 0, 0, $m, $d + 1, $y ) - 1,
281
+ )
282
+ );
283
+
284
+ $occ_unknown = count( $occ_unknown ) ? $occ_unknown[0] : null;
285
+ if ( ! empty( $occ_unknown ) ) {
286
+ // Update existing record not exists user.
287
+ $this->IncrementLoginFailure( $ip, $site_id, false );
288
+ $new = $occ_unknown->GetMetaValue( 'Attempts', 0 ) + 1;
289
+
290
+ if ( 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
291
+ $link_file = $this->WriteLog( $new, $username );
292
+ }
293
+
294
+ if ( -1 !== (int) $this->GetVisitorLoginFailureLogLimit()
295
+ && $new > $this->GetVisitorLoginFailureLogLimit() ) {
296
+ $new = $this->GetVisitorLoginFailureLogLimit() . '+';
297
+ }
298
+
299
+ $occ_unknown->UpdateMetaValue( 'Attempts', $new );
300
+ if ( ! empty( $link_file ) && 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
301
+ $occ_unknown->UpdateMetaValue( 'LogFileLink', $link_file );
302
+ } else {
303
+ $link_file = site_url() . '/wp-admin/admin.php?page=wsal-togglealerts#tab-users-profiles---activity';
304
+ $occ_unknown->UpdateMetaValue( 'LogFileLink', $link_file );
305
+ }
306
+ $occ_unknown->created_on = null;
307
+ $occ_unknown->Save();
308
+ } else {
309
+ $link_file = site_url() . '/wp-admin/admin.php?page=wsal-togglealerts#tab-users-profiles---activity';
310
+ $log_file_text = ' in a log file';
311
+ if ( 'on' === $this->plugin->GetGlobalOption( 'log-visitor-failed-login' ) ) {
312
+ $link_file = $this->WriteLog( 1, $username );
313
+ $log_file_text = ' with the usernames used during these failed login attempts';
314
+ }
315
+
316
+ // Create a new record not exists user.
317
+ $this->plugin->alerts->Trigger(
318
+ $new_alert_code, array(
319
+ 'Attempts' => 1,
320
+ 'LogFileLink' => $link_file,
321
+ 'LogFileText' => $log_file_text,
322
+ )
323
+ );
324
+ }
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Event changed password.
330
+ *
331
+ * @param WP_User $user - User object.
332
+ * @param string $new_pass - New Password.
333
+ */
334
+ public function EventPasswordReset( $user, $new_pass ) {
335
+ if ( ! empty( $user ) ) {
336
+ $user_roles = $this->plugin->settings->GetCurrentUserRoles( $user->roles );
337
+ $this->plugin->alerts->Trigger(
338
+ 4003, array(
339
+ 'Username' => $user->user_login,
340
+ 'CurrentUserRoles' => $user_roles,
341
+ ), true
342
+ );
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Event login blocked.
348
+ *
349
+ * @param string $username - Username.
350
+ */
351
+ public function EventLoginBlocked( $username ) {
352
+ $user = get_user_by( 'login', $username );
353
+ $user_roles = $this->plugin->settings->GetCurrentUserRoles( $user->roles );
354
+
355
+ if ( $this->plugin->settings->IsLoginSuperAdmin( $username ) ) {
356
+ $user_roles[] = 'superadmin';
357
+ }
358
+ $this->plugin->alerts->Trigger(
359
+ 1004, array(
360
+ 'Username' => $username,
361
+ 'CurrentUserRoles' => $user_roles,
362
+ ), true
363
+ );
364
+ }
365
+
366
+ /**
367
+ * Write log file.
368
+ *
369
+ * @param int $attempts - Number of attempt.
370
+ * @param string $username - Username.
371
+ * @author Ashar Irfan
372
+ * @since 2.6.9
373
+ */
374
+ private function WriteLog( $attempts, $username = '' ) {
375
+ $name_file = null;
376
+
377
+ // Create/Append to the log file.
378
+ $data = 'Attempts: ' . $attempts . ' — Username: ' . $username;
379
+
380
+ $upload_dir = wp_upload_dir();
381
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/failed-logins/';
382
+ $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/failed-logins/';
383
+
384
+ // Check directory.
385
+ if ( $this->CheckDirectory( $uploads_dir_path ) ) {
386
+ $filename = 'failed_logins_usernames_' . date( 'Ymd' ) . '.log';
387
+ $fp = $uploads_dir_path . $filename;
388
+ $name_file = $uploads_url . $filename;
389
+ if ( ! $file = fopen( $fp, 'a' ) ) {
390
+ $i = 1;
391
+ $file_opened = false;
392
+ do {
393
+ $fp2 = substr( $fp, 0, -4 ) . '_' . $i . '.log';
394
+ if ( ! file_exists( $fp2 ) ) {
395
+ if ( $file = fopen( $fp2, 'a' ) ) {
396
+ $file_opened = true;
397
+ $name_file = $uploads_url . substr( $name_file, 0, -4 ) . '_' . $i . '.log';
398
+ }
399
+ } else {
400
+ $latest_filename = $this->GetLastModified( $uploads_dir_path, $filename );
401
+ $fp_last = $uploads_dir_path . $latest_filename;
402
+ if ( $file = fopen( $fp_last, 'a' ) ) {
403
+ $file_opened = true;
404
+ $name_file = $uploads_url . $latest_filename;
405
+ }
406
+ }
407
+ $i++;
408
+ } while ( ! $file_opened );
409
+ }
410
+ fwrite( $file, sprintf( "%s\n", $data ) );
411
+ fclose( $file );
412
+ }
413
+
414
+ return $name_file;
415
+ }
416
+
417
+ /**
418
+ * Get the latest file modified.
419
+ *
420
+ * @param string $uploads_dir_path - Uploads directory path.
421
+ * @param string $filename - File name.
422
+ * @return string $latest_filename - File name.
423
+ */
424
+ private function GetLastModified( $uploads_dir_path, $filename ) {
425
+ $filename = substr( $filename, 0, -4 );
426
+ $latest_mtime = 0;
427
+ $latest_filename = '';
428
+ if ( $handle = opendir( $uploads_dir_path ) ) {
429
+ while ( false !== ($entry = readdir( $handle )) ) {
430
+ if ( '.' != $entry && '..' != $entry ) {
431
+ $entry = strip_tags( $entry ); // Strip HTML Tags.
432
+ $entry = preg_replace( '/[\r\n\t ]+/', ' ', $entry ); // Remove Break/Tabs/Return Carriage.
433
+ $entry = preg_replace( '/[\"\*\/\:\<\>\?\'\|]+/', ' ', $entry ); // Remove Illegal Chars for folder and filename.
434
+ if ( preg_match( '/^' . $filename . '/i', $entry ) > 0 ) {
435
+ if ( filemtime( $uploads_dir_path . $entry ) > $latest_mtime ) {
436
+ $latest_mtime = filemtime( $uploads_dir_path . $entry );
437
+ $latest_filename = $entry;
438
+ }
439
+ }
440
+ }
441
+ }
442
+ closedir( $handle );
443
+ }
444
+ return $latest_filename;
445
+ }
446
  }
classes/Sensors/Menus.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Menus sensor.
6
  *
7
  * 2078 User created new menu
@@ -13,518 +25,637 @@
13
  * 2084 User changed name of a menu
14
  * 2085 User changed order of the objects in a menu
15
  * 2089 User moved objects as a sub-item
 
 
 
16
  */
17
- class WSAL_Sensors_Menus extends WSAL_AbstractSensor
18
- {
19
- protected $_OldMenu = null;
20
- protected $_OldMenuTerms = array();
21
- protected $_OldMenuItems = array();
22
- protected $_OldMenuLocations = null;
23
-
24
- /**
25
- * Listening to events using WP hooks.
26
- */
27
- public function HookEvents()
28
- {
29
- add_action('wp_create_nav_menu', array($this, 'CreateMenu'), 10, 2);
30
- add_action('wp_delete_nav_menu', array($this, 'DeleteMenu'), 10, 1);
31
- add_action('wp_update_nav_menu', array($this, 'UpdateMenu'), 10, 2);
32
-
33
- add_action('wp_update_nav_menu_item', array($this, 'UpdateMenuItem'), 10, 3);
34
- add_action('admin_menu', array($this, 'ManageMenuLocations'));
35
-
36
- add_action('admin_init', array($this, 'EventAdminInit'));
37
- // Customizer trigger
38
- add_action('customize_register', array($this, 'CustomizeInit'));
39
- add_action('customize_save_after', array($this, 'CustomizeSave'));
40
- }
41
-
42
- /**
43
- * Menu item updated.
44
- */
45
- public function UpdateMenuItem($menu_id, $menu_item_db_id, $args)
46
- {
47
- $oldMenuItems = array();
48
- if (isset($_POST['menu-item-title']) && isset($_POST['menu-name'])) {
49
- $is_changed_order = false;
50
- $is_sub_item = false;
51
- $newMenuItems = array_keys($_POST['menu-item-title']);
52
- $items = wp_get_nav_menu_items($menu_id);
53
- if (!empty($this->_OldMenuItems)) {
54
- foreach ($this->_OldMenuItems as $oldItem) {
55
- if ($oldItem['menu_id'] == $menu_id) {
56
- $item_id = $oldItem['item_id'];
57
- if ($item_id == $menu_item_db_id) {
58
- if ($oldItem['menu_order'] != $args['menu-item-position']) {
59
- $is_changed_order = true;
60
- }
61
- if (!empty($args['menu-item-parent-id'])) {
62
- $is_sub_item = true;
63
- }
64
- if (!empty($args['menu-item-title']) && $oldItem['title'] != $args['menu-item-title']) {
65
- $this->EventModifiedItems($_POST['menu-item-object'][$menu_item_db_id], $_POST['menu-item-title'][$menu_item_db_id], $_POST['menu-name']);
66
- }
67
- }
68
- $oldMenuItems[$item_id] = array("type" => $oldItem['object'], "title" => $oldItem['title'], "parent" => $oldItem['menu_item_parent']);
69
- }
70
- }
71
- }
72
- if ($is_changed_order) {
73
- $item_name = $oldMenuItems[$menu_item_db_id]['title'];
74
- $this->EventChangeOrder($item_name, $oldItem['menu_name']);
75
- }
76
- if ($is_sub_item) {
77
- $item_parent_id = $args['menu-item-parent-id'];
78
- $item_name = $oldMenuItems[$menu_item_db_id]['title'];
79
- if ($oldMenuItems[$menu_item_db_id]['parent'] != $item_parent_id) {
80
- $parent_name = $oldMenuItems[$item_parent_id]['title'];
81
- $this->EventChangeSubItem($item_name, $parent_name, $_POST['menu-name']);
82
- }
83
- }
84
-
85
- $addedItems = array_diff($newMenuItems, array_keys($oldMenuItems));
86
- // Add Items to the menu
87
- if (count($addedItems) > 0) {
88
- if (in_array($menu_item_db_id, $addedItems)) {
89
- $this->EventAddItems($_POST['menu-item-object'][$menu_item_db_id], $_POST['menu-item-title'][$menu_item_db_id], $_POST['menu-name']);
90
- }
91
- }
92
- $removedItems = array_diff(array_keys($oldMenuItems), $newMenuItems);
93
- // Remove items from the menu
94
- if (count($removedItems) > 0) {
95
- if (array_search($menu_item_db_id, $newMenuItems) == (count($newMenuItems)-1)) {
96
- foreach ($removedItems as $removed_item_id) {
97
- $this->EventRemoveItems($oldMenuItems[$removed_item_id]['type'], $oldMenuItems[$removed_item_id]['title'], $_POST['menu-name']);
98
- }
99
- }
100
- }
101
- }
102
- }
103
-
104
- /**
105
- * New menu created.
106
- * @param array $menu_data data
107
- */
108
- public function CreateMenu($term_id, $menu_data)
109
- {
110
- $this->plugin->alerts->Trigger(2078, array(
111
- 'MenuName' => $menu_data['menu-name']
112
- ));
113
- }
114
-
115
- /**
116
- * New menu created.
117
- * @global array $_POST data post
118
- */
119
- public function ManageMenuLocations()
120
- {
121
- // Manage Location tab
122
- if (isset($_POST['menu-locations'])) {
123
- $new_locations = $_POST['menu-locations'];
124
- if (isset($new_locations['primary'])) {
125
- $this->LocationSetting($new_locations['primary'], 'primary');
126
- }
127
- if (isset($new_locations['social'])) {
128
- $this->LocationSetting($new_locations['social'], 'social');
129
- }
130
- }
131
- }
132
-
133
- /**
134
- * Menu location.
135
- * @param integer $new_location location
136
- */
137
- private function LocationSetting($new_location, $type)
138
- {
139
- $old_locations = get_nav_menu_locations();
140
- if ($new_location != 0) {
141
- $menu = wp_get_nav_menu_object($new_location);
142
- if (!empty($old_locations[$type]) && $old_locations[$type] != $new_location) {
143
- $this->EventMenuSetting($menu->name, "Enabled", "Location: ".$type." menu");
144
- }
145
- } else {
146
- if (!empty($old_locations[$type])) {
147
- $menu = wp_get_nav_menu_object($old_locations[$type]);
148
- $this->EventMenuSetting($menu->name, "Disabled", "Location: ".$type." menu");
149
- }
150
- }
151
- }
152
-
153
- /**
154
- * Menu deleted.
155
- */
156
- public function DeleteMenu($term_id)
157
- {
158
- if ($this->_OldMenu) {
159
- $this->plugin->alerts->Trigger(2081, array(
160
- 'MenuName' => $this->_OldMenu->name
161
- ));
162
- }
163
- }
164
-
165
- /**
166
- * Menu updated.
167
- * @param array $menu_data (Optional) data
168
- */
169
- public function UpdateMenu($menu_id, $menu_data = null)
170
- {
171
- if (!empty($menu_data)) {
172
- $contentNamesOld = array();
173
- $contentTypesOld = array();
174
- $contentOrderOld = array();
175
-
176
- $items = wp_get_nav_menu_items($menu_id);
177
- if (!empty($items)) {
178
- foreach ($items as $item) {
179
- array_push($contentNamesOld, $item->title);
180
- array_push($contentTypesOld, $item->object);
181
- $contentOrderOld[$item->ID] = $item->menu_order;
182
- }
183
- }
184
- // Menu changed name
185
- if (!empty($this->_OldMenuTerms) && isset($_POST['menu']) && isset($_POST['menu-name'])) {
186
- foreach ($this->_OldMenuTerms as $oldMenuTerm) {
187
- if ($oldMenuTerm['term_id'] == $_POST['menu']) {
188
- if ($oldMenuTerm['name'] != $_POST['menu-name']) {
189
- $this->EventChangeName($oldMenuTerm['name'], $_POST['menu-name']);
190
- } else {
191
- // Remove the last menu item
192
- if (count($contentNamesOld) == 1 && count($contentTypesOld) == 1) {
193
- $this->EventRemoveItems($contentTypesOld[0], $contentNamesOld[0], $_POST['menu-name']);
194
- }
195
- }
196
- }
197
- }
198
- }
199
- // Enable/Disable menu setting
200
- $nav_menu_options = maybe_unserialize(get_option('nav_menu_options'));
201
- $auto_add = null;
202
- if (isset($nav_menu_options['auto_add'])) {
203
- if (in_array($menu_id, $nav_menu_options['auto_add'])) {
204
- if (empty($_POST['auto-add-pages'])) {
205
- $auto_add = "Disabled";
206
- }
207
- } else {
208
- if (isset($_POST['auto-add-pages'])) {
209
- $auto_add = "Enabled";
210
- }
211
- }
212
- } else {
213
- if (isset($_POST['auto-add-pages'])) {
214
- $auto_add = "Enabled";
215
- }
216
- }
217
- // Alert 2082 Auto add pages
218
- if (!empty($auto_add)) {
219
- $this->EventMenuSetting($menu_data['menu-name'], $auto_add, "Auto add pages");
220
- }
221
-
222
- $nav_menu_locations = get_nav_menu_locations();
223
-
224
- $locationPrimary = null;
225
- if (isset($this->_OldMenuLocations['primary']) && isset($nav_menu_locations['primary'])) {
226
- if ($nav_menu_locations['primary'] == $menu_id && $this->_OldMenuLocations['primary'] != $nav_menu_locations['primary']) {
227
- $locationPrimary = "Enabled";
228
- }
229
- } elseif (empty($this->_OldMenuLocations['primary']) && isset($nav_menu_locations['primary'])) {
230
- if ($nav_menu_locations['primary'] == $menu_id) {
231
- $locationPrimary = "Enabled";
232
- }
233
- } elseif (isset($this->_OldMenuLocations['primary']) && empty($nav_menu_locations['primary'])) {
234
- if ($this->_OldMenuLocations['primary'] == $menu_id) {
235
- $locationPrimary = "Disabled";
236
- }
237
- }
238
- // Alert 2082 Primary menu
239
- if (!empty($locationPrimary)) {
240
- $this->EventMenuSetting($menu_data['menu-name'], $locationPrimary, "Location: primary menu");
241
- }
242
-
243
- $locationSocial = null;
244
- if (isset($this->_OldMenuLocations['social']) && isset($nav_menu_locations['social'])) {
245
- if ($nav_menu_locations['social'] == $menu_id && $this->_OldMenuLocations['social'] != $nav_menu_locations['social']) {
246
- $locationSocial = "Enabled";
247
- }
248
- } elseif (empty($this->_OldMenuLocations['social']) && isset($nav_menu_locations['social'])) {
249
- if ($nav_menu_locations['social'] == $menu_id) {
250
- $locationSocial = "Enabled";
251
- }
252
- } elseif (isset($this->_OldMenuLocations['social']) && empty($nav_menu_locations['social'])) {
253
- if ($this->_OldMenuLocations['social'] == $menu_id) {
254
- $locationSocial = "Disabled";
255
- }
256
- }
257
- // Alert 2082 Social links menu
258
- if (!empty($locationSocial)) {
259
- $this->EventMenuSetting($menu_data['menu-name'], $locationSocial, "Location: social menu");
260
- }
261
- }
262
- }
263
-
264
- /**
265
- * Set old menu terms and items.
266
- */
267
- private function BuildOldMenuTermsAndItems()
268
- {
269
- $menus = wp_get_nav_menus();
270
- if (!empty($menus)) {
271
- foreach ($menus as $menu) {
272
- array_push($this->_OldMenuTerms, array("term_id" => $menu->term_id, "name" => $menu->name));
273
- $items = wp_get_nav_menu_items($menu->term_id);
274
- if (!empty($items)) {
275
- foreach ($items as $item) {
276
- array_push($this->_OldMenuItems, array(
277
- "menu_id" => $menu->term_id,
278
- 'item_id' => $item->ID,
279
- 'title' => $item->title,
280
- 'object' => $item->object,
281
- 'menu_name' => $menu->name,
282
- 'menu_order' => $item->menu_order,
283
- 'url' => $item->url,
284
- 'menu_item_parent' => $item->menu_item_parent
285
- ));
286
- }
287
- }
288
- }
289
- }
290
- }
291
-
292
- /**
293
- * Triggered when a user accesses the admin area.
294
- */
295
- public function EventAdminInit()
296
- {
297
- $is_nav_menu = basename($_SERVER['SCRIPT_NAME']) == 'nav-menus.php';
298
- if ($is_nav_menu) {
299
- if (isset($_GET['action']) && $_GET['action'] == 'delete') {
300
- if (isset($_GET['menu'])) {
301
- $this->_OldMenu = wp_get_nav_menu_object($_GET['menu']);
302
- }
303
- } else {
304
- $this->BuildOldMenuTermsAndItems();
305
- }
306
- $this->_OldMenuLocations = get_nav_menu_locations();
307
- }
308
- }
309
-
310
- /**
311
- * Customize set old data.
312
- */
313
- public function CustomizeInit()
314
- {
315
- $this->BuildOldMenuTermsAndItems();
316
- $this->_OldMenuLocations = get_nav_menu_locations();
317
- }
318
-
319
- /**
320
- * Customize Events Function.
321
- */
322
- public function CustomizeSave()
323
- {
324
- $updateMenus = array();
325
- $menus = wp_get_nav_menus();
326
- if (!empty($menus)) {
327
- foreach ($menus as $menu) {
328
- array_push($updateMenus, array("term_id" => $menu->term_id, "name" => $menu->name));
329
- }
330
- }
331
- // Deleted Menu
332
- if (isset($updateMenus) && isset($this->_OldMenuTerms)) {
333
- $terms = array_diff(array_map('serialize', $this->_OldMenuTerms), array_map('serialize', $updateMenus));
334
- $terms = array_map('unserialize', $terms);
335
-
336
- if (isset($terms) && count($terms) > 0) {
337
- foreach ($terms as $term) {
338
- $this->plugin->alerts->Trigger(2081, array(
339
- 'MenuName' => $term['name']
340
- ));
341
- }
342
- }
343
- }
344
- if (isset($_POST['action']) && $_POST['action'] == 'customize_save') {
345
- if (isset($_POST['wp_customize'], $_POST['customized'])) {
346
- $customized = json_decode(wp_unslash($_POST['customized']), true);
347
- if (is_array($customized)) {
348
- foreach ($customized as $key => $value) {
349
- if (!empty($value['nav_menu_term_id'])) {
350
- $is_occurred_event = false;
351
- $menu = wp_get_nav_menu_object($value['nav_menu_term_id']);
352
- $content_name = !empty($value['title']) ? $value['title'] : "no title";
353
- if (!empty($this->_OldMenuItems)) {
354
- foreach ($this->_OldMenuItems as $old_item) {
355
- $item_id = substr(trim($key, ']'), 14);
356
- if ($old_item['item_id'] == $item_id) {
357
- // Modified Items in the menu
358
- if ($old_item['title'] != $content_name) {
359
- $is_occurred_event = true;
360
- $this->EventModifiedItems($value['type_label'], $content_name, $menu->name);
361
- }
362
- // Moved as a sub-item
363
- if ($old_item['menu_item_parent'] != $value['menu_item_parent'] && $value['menu_item_parent'] != 0) {
364
- $is_occurred_event = true;
365
- $parent_name = $this->GetItemName($value['nav_menu_term_id'], $value['menu_item_parent']);
366
- $this->EventChangeSubItem($content_name, $parent_name, $menu->name);
367
- }
368
- // Changed order of the objects in a menu
369
- if ($old_item['menu_order'] != $value['position']) {
370
- $is_occurred_event = true;
371
- $this->EventChangeOrder($content_name, $menu->name);
372
- }
373
- }
374
- }
375
- }
376
- // Add Items to the menu
377
- if (!$is_occurred_event) {
378
- $menu_name = !empty($customized['new_menu_name']) ? $customized['new_menu_name'] : $menu->name;
379
- $this->EventAddItems($value['type_label'], $content_name, $menu_name);
380
- }
381
- } else {
382
- // Menu changed name
383
- if (isset($updateMenus) && isset($this->_OldMenuTerms)) {
384
- foreach ($this->_OldMenuTerms as $old_menu) {
385
- foreach ($updateMenus as $update_menu) {
386
- if ($old_menu['term_id'] == $update_menu['term_id'] && $old_menu['name'] != $update_menu['name']) {
387
- $this->EventChangeName($old_menu['name'], $update_menu['name']);
388
- }
389
- }
390
- }
391
- }
392
- // Setting Auto add pages
393
- if (!empty($value) && isset($value['auto_add'])) {
394
- if ($value['auto_add']) {
395
- $this->EventMenuSetting($value['name'], 'Enabled', "Auto add pages");
396
- } else {
397
- $this->EventMenuSetting($value['name'], 'Disabled', "Auto add pages");
398
- }
399
- }
400
- // Setting Location
401
- if (false !== strpos($key, 'nav_menu_locations[')) {
402
- $loc = substr(trim($key, ']'), 19);
403
- if (!empty($value)) {
404
- $menu = wp_get_nav_menu_object($value);
405
- $menu_name = !empty($customized['new_menu_name']) ? $customized['new_menu_name'] : (!empty($menu) ? $menu->name : '');
406
- $this->EventMenuSetting($menu_name, "Enabled", "Location: ".$loc." menu");
407
- } else {
408
- if (!empty($this->_OldMenuLocations[$loc])) {
409
- $menu = wp_get_nav_menu_object($this->_OldMenuLocations[$loc]);
410
- $menu_name = !empty($customized['new_menu_name']) ? $customized['new_menu_name'] : (!empty($menu) ? $menu->name : '');
411
- $this->EventMenuSetting($menu_name, "Disabled", "Location: ".$loc." menu");
412
- }
413
- }
414
- }
415
- // Remove items from the menu
416
- if (false !== strpos($key, 'nav_menu_item[')) {
417
- $item_id = substr(trim($key, ']'), 14);
418
- if (!empty($this->_OldMenuItems)) {
419
- foreach ($this->_OldMenuItems as $old_item) {
420
- if ($old_item['item_id'] == $item_id) {
421
- $this->EventRemoveItems($old_item['object'], $old_item['title'], $old_item['menu_name']);
422
- }
423
- }
424
- }
425
- }
426
- }
427
- }
428
- }
429
- }
430
- }
431
- }
432
-
433
- /**
434
- * Added content to a menu.
435
- */
436
- private function EventAddItems($content_type, $content_name, $menu_name)
437
- {
438
- $this->plugin->alerts->Trigger(2079, array(
439
- 'ContentType' => $content_type,
440
- 'ContentName' => $content_name,
441
- 'MenuName' => $menu_name
442
- ));
443
- }
444
-
445
- /**
446
- * Removed content from a menu.
447
- */
448
- private function EventRemoveItems($content_type, $content_name, $menu_name)
449
- {
450
- $this->plugin->alerts->Trigger(2080, array(
451
- 'ContentType' => $content_type,
452
- 'ContentName' => $content_name,
453
- 'MenuName' => $menu_name
454
- ));
455
- }
456
-
457
- /**
458
- * Changed menu setting.
459
- */
460
- private function EventMenuSetting($menu_name, $status, $menu_setting)
461
- {
462
- $this->plugin->alerts->Trigger(2082, array(
463
- 'Status' => $status,
464
- 'MenuSetting' => $menu_setting,
465
- 'MenuName' => $menu_name
466
- ));
467
- }
468
-
469
- /**
470
- * Modified content in a menu.
471
- */
472
- private function EventModifiedItems($content_type, $content_name, $menu_name)
473
- {
474
- $this->plugin->alerts->Trigger(2083, array(
475
- 'ContentType' => $content_type,
476
- 'ContentName' => $content_name,
477
- 'MenuName' => $menu_name
478
- ));
479
- }
480
-
481
- /**
482
- * Changed name of a menu.
483
- */
484
- private function EventChangeName($old_menu_name, $new_menu_name)
485
- {
486
- $this->plugin->alerts->Trigger(2084, array(
487
- 'OldMenuName' => $old_menu_name,
488
- 'NewMenuName' => $new_menu_name
489
- ));
490
- }
491
-
492
- /**
493
- * Changed order of the objects in a menu.
494
- */
495
- private function EventChangeOrder($item_name, $menu_name)
496
- {
497
- $this->plugin->alerts->Trigger(2085, array(
498
- 'ItemName' => $item_name,
499
- 'MenuName' => $menu_name
500
- ));
501
- }
502
-
503
- /**
504
- * Moved objects as a sub-item.
505
- */
506
- private function EventChangeSubItem($item_name, $parent_name, $menu_name)
507
- {
508
- $this->plugin->alerts->Trigger(2089, array(
509
- 'ItemName' => $item_name,
510
- 'ParentName' => $parent_name,
511
- 'MenuName' => $menu_name
512
- ));
513
- }
514
-
515
- /**
516
- * Get menu item name.
517
- */
518
- private function GetItemName($term_id, $item_id)
519
- {
520
- $item_name = '';
521
- $menu_items = wp_get_nav_menu_items($term_id);
522
- foreach ($menu_items as $menu_item) {
523
- if ($menu_item->ID == $item_id) {
524
- $item_name = $menu_item->title;
525
- break;
526
- }
527
- }
528
- return $item_name;
529
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  }
1
  <?php
2
  /**
3
+ * Sensor: Menus
4
+ *
5
+ * Menus sensor file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Menus sensor.
18
  *
19
  * 2078 User created new menu
25
  * 2084 User changed name of a menu
26
  * 2085 User changed order of the objects in a menu
27
  * 2089 User moved objects as a sub-item
28
+ *
29
+ * @package Wsal
30
+ * @subpackage Sensors
31
  */
32
+ class WSAL_Sensors_Menus extends WSAL_AbstractSensor {
33
+
34
+ /**
35
+ * Menu object.
36
+ *
37
+ * @var object
38
+ */
39
+ protected $_old_menu = null;
40
+
41
+ /**
42
+ * Old Menu objects.
43
+ *
44
+ * @var array
45
+ */
46
+ protected $_old_menu_terms = array();
47
+
48
+ /**
49
+ * Old Menu Items.
50
+ *
51
+ * @var array
52
+ */
53
+ protected $_old_menu_items = array();
54
+
55
+ /**
56
+ * Old Menu Locations.
57
+ *
58
+ * @var array
59
+ */
60
+ protected $_old_menu_locations = null;
61
+
62
+ /**
63
+ * Listening to events using WP hooks.
64
+ */
65
+ public function HookEvents() {
66
+ add_action( 'wp_create_nav_menu', array( $this, 'CreateMenu' ), 10, 2 );
67
+ add_action( 'wp_delete_nav_menu', array( $this, 'DeleteMenu' ), 10, 1 );
68
+ add_action( 'wp_update_nav_menu', array( $this, 'UpdateMenu' ), 10, 2 );
69
+
70
+ add_action( 'wp_update_nav_menu_item', array( $this, 'UpdateMenuItem' ), 10, 3 );
71
+ add_action( 'admin_menu', array( $this, 'ManageMenuLocations' ) );
72
+ add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
73
+
74
+ // Customizer trigger.
75
+ add_action( 'customize_register', array( $this, 'CustomizeInit' ) );
76
+ add_action( 'customize_save_after', array( $this, 'CustomizeSave' ) );
77
+ }
78
+
79
+ /**
80
+ * Menu item updated.
81
+ *
82
+ * @param int $menu_id - Menu ID.
83
+ * @param int $menu_item_db_id - Menu item DB ID.
84
+ * @param array $args - An array of items used to update menu.
85
+ */
86
+ public function UpdateMenuItem( $menu_id, $menu_item_db_id, $args ) {
87
+ // Filter $_POST global array for security.
88
+ $post_array = filter_input_array( INPUT_POST );
89
+
90
+ $old_menu_items = array();
91
+ if ( isset( $post_array['menu-item-title'] ) && isset( $post_array['menu-name'] ) ) {
92
+ $is_changed_order = false;
93
+ $is_sub_item = false;
94
+ $new_menu_items = array_keys( $post_array['menu-item-title'] );
95
+ $items = wp_get_nav_menu_items( $menu_id );
96
+ if ( ! empty( $this->_old_menu_items ) ) {
97
+ foreach ( $this->_old_menu_items as $old_item ) {
98
+ if ( $old_item['menu_id'] == $menu_id ) {
99
+ $item_id = $old_item['item_id'];
100
+ if ( $item_id == $menu_item_db_id ) {
101
+ if ( $old_item['menu_order'] != $args['menu-item-position'] ) {
102
+ $is_changed_order = true;
103
+ }
104
+ if ( ! empty( $args['menu-item-parent-id'] ) ) {
105
+ $is_sub_item = true;
106
+ }
107
+ if ( ! empty( $args['menu-item-title'] ) && $old_item['title'] != $args['menu-item-title'] ) {
108
+ // Verify nonce.
109
+ if ( ! wp_verify_nonce( $post_array['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
110
+ return false;
111
+ }
112
+
113
+ $this->EventModifiedItems( $post_array['menu-item-object'][ $menu_item_db_id ], $post_array['menu-item-title'][ $menu_item_db_id ], $post_array['menu-name'] );
114
+ }
115
+ }
116
+ $old_menu_items[ $item_id ] = array(
117
+ 'type' => $old_item['object'],
118
+ 'title' => $old_item['title'],
119
+ 'parent' => $old_item['menu_item_parent'],
120
+ );
121
+ }
122
+ }
123
+ }
124
+ if ( $is_changed_order && wp_verify_nonce( $post_array['meta-box-order-nonce'], 'meta-box-order' ) ) {
125
+ $item_name = $old_menu_items[ $menu_item_db_id ]['title'];
126
+ $this->EventChangeOrder( $item_name, $old_item['menu_name'] );
127
+ }
128
+ if ( $is_sub_item && wp_verify_nonce( $post_array['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
129
+ $item_parent_id = $args['menu-item-parent-id'];
130
+ $item_name = $old_menu_items[ $menu_item_db_id ]['title'];
131
+ if ( $old_menu_items[ $menu_item_db_id ]['parent'] != $item_parent_id ) {
132
+ $parent_name = $old_menu_items[ $item_parent_id ]['title'];
133
+ $this->EventChangeSubItem( $item_name, $parent_name, $post_array['menu-name'] );
134
+ }
135
+ }
136
+ $added_items = array_diff( $new_menu_items, array_keys( $old_menu_items ) );
137
+
138
+ // Add Items to the menu.
139
+ if ( count( $added_items ) > 0 && wp_verify_nonce( $post_array['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
140
+ if ( in_array( $menu_item_db_id, $added_items ) ) {
141
+ $this->EventAddItems( $post_array['menu-item-object'][ $menu_item_db_id ], $post_array['menu-item-title'][ $menu_item_db_id ], $post_array['menu-name'] );
142
+ }
143
+ }
144
+ $removed_items = array_diff( array_keys( $old_menu_items ), $new_menu_items );
145
+
146
+ // Remove items from the menu.
147
+ if ( count( $removed_items ) > 0 && wp_verify_nonce( $post_array['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
148
+ if ( array_search( $menu_item_db_id, $new_menu_items ) == (count( $new_menu_items ) - 1) ) {
149
+ foreach ( $removed_items as $removed_item_id ) {
150
+ $this->EventRemoveItems( $old_menu_items[ $removed_item_id ]['type'], $old_menu_items[ $removed_item_id ]['title'], $post_array['menu-name'] );
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * New menu created.
159
+ *
160
+ * @param int $term_id - Term ID.
161
+ * @param array $menu_data - Menu data.
162
+ */
163
+ public function CreateMenu( $term_id, $menu_data ) {
164
+ $this->plugin->alerts->Trigger(
165
+ 2078, array(
166
+ 'MenuName' => $menu_data['menu-name'],
167
+ )
168
+ );
169
+ }
170
+
171
+ /**
172
+ * New menu created.
173
+ *
174
+ * @global array $_POST Data post.
175
+ */
176
+ public function ManageMenuLocations() {
177
+ // Filter $_POST global array for security.
178
+ $post_array = filter_input_array( INPUT_POST );
179
+
180
+ // Verify nonce.
181
+ if ( isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'save-menu-locations' ) ) {
182
+ return false;
183
+ }
184
+
185
+ // Manage Location tab.
186
+ if ( isset( $post_array['menu-locations'] ) ) {
187
+ $new_locations = $post_array['menu-locations'];
188
+ if ( isset( $new_locations['primary'] ) ) {
189
+ $this->LocationSetting( $new_locations['primary'], 'primary' );
190
+ }
191
+ if ( isset( $new_locations['social'] ) ) {
192
+ $this->LocationSetting( $new_locations['social'], 'social' );
193
+ }
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Menu location.
199
+ *
200
+ * @param integer $new_location - New location.
201
+ * @param string $type - Location type.
202
+ */
203
+ private function LocationSetting( $new_location, $type ) {
204
+ $old_locations = get_nav_menu_locations();
205
+ if ( 0 != $new_location ) {
206
+ $menu = wp_get_nav_menu_object( $new_location );
207
+ if ( ! empty( $old_locations[ $type ] ) && $old_locations[ $type ] != $new_location ) {
208
+ $this->EventMenuSetting( $menu->name, 'Enabled', 'Location: ' . $type . ' menu' );
209
+ }
210
+ } else {
211
+ if ( ! empty( $old_locations[ $type ] ) ) {
212
+ $menu = wp_get_nav_menu_object( $old_locations[ $type ] );
213
+ $this->EventMenuSetting( $menu->name, 'Disabled', 'Location: ' . $type . ' menu' );
214
+ }
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Menu deleted.
220
+ *
221
+ * @param int $term_id - Term ID.
222
+ */
223
+ public function DeleteMenu( $term_id ) {
224
+ if ( $this->_old_menu ) {
225
+ $this->plugin->alerts->Trigger(
226
+ 2081, array(
227
+ 'MenuName' => $this->_old_menu->name,
228
+ )
229
+ );
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Menu updated.
235
+ *
236
+ * @param int $menu_id - Menu ID.
237
+ * @param array $menu_data (Optional) Menu data.
238
+ */
239
+ public function UpdateMenu( $menu_id, $menu_data = null ) {
240
+ if ( ! empty( $menu_data ) ) {
241
+ $content_names_old = array();
242
+ $content_types_old = array();
243
+ $content_order_old = array();
244
+
245
+ $items = wp_get_nav_menu_items( $menu_id );
246
+ if ( ! empty( $items ) ) {
247
+ foreach ( $items as $item ) {
248
+ array_push( $content_names_old, $item->title );
249
+ array_push( $content_types_old, $item->object );
250
+ $content_order_old[ $item->ID ] = $item->menu_order;
251
+ }
252
+ }
253
+
254
+ // Filter $_POST global array for security.
255
+ $post_array = filter_input_array( INPUT_POST );
256
+
257
+ // Menu changed name.
258
+ if ( ! empty( $this->_old_menu_terms ) && isset( $post_array['menu'] ) && isset( $post_array['menu-name'] ) ) {
259
+ foreach ( $this->_old_menu_terms as $old_menu_term ) {
260
+ if ( $old_menu_term['term_id'] == $post_array['menu'] && wp_verify_nonce( $post_array['update-nav-menu-nonce'], 'update-nav_menu' ) ) {
261
+ if ( $old_menu_term['name'] != $post_array['menu-name'] ) {
262
+ $this->EventChangeName( $old_menu_term['name'], $post_array['menu-name'] );
263
+ } else {
264
+ // Remove the last menu item.
265
+ if ( count( $content_names_old ) == 1 && count( $content_types_old ) == 1 ) {
266
+ $this->EventRemoveItems( $content_types_old[0], $content_names_old[0], $post_array['menu-name'] );
267
+ }
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ // Enable/Disable menu setting.
274
+ $nav_menu_options = maybe_unserialize( get_option( 'nav_menu_options' ) );
275
+ $auto_add = null;
276
+ if ( isset( $nav_menu_options['auto_add'] ) ) {
277
+ if ( in_array( $menu_id, $nav_menu_options['auto_add'] ) ) {
278
+ if ( empty( $post_array['auto-add-pages'] ) ) {
279
+ $auto_add = 'Disabled';
280
+ }
281
+ } else {
282
+ if ( isset( $post_array['auto-add-pages'] ) ) {
283
+ $auto_add = 'Enabled';
284
+ }
285
+ }
286
+ } else {
287
+ if ( isset( $post_array['auto-add-pages'] ) ) {
288
+ $auto_add = 'Enabled';
289
+ }
290
+ }
291
+
292
+ // Alert 2082 Auto add pages.
293
+ if ( ! empty( $auto_add ) ) {
294
+ $this->EventMenuSetting( $menu_data['menu-name'], $auto_add, 'Auto add pages' );
295
+ }
296
+
297
+ $nav_menu_locations = get_nav_menu_locations();
298
+
299
+ $location_primary = null;
300
+ if ( isset( $this->_old_menu_locations['primary'] ) && isset( $nav_menu_locations['primary'] ) ) {
301
+ if ( $nav_menu_locations['primary'] == $menu_id && $this->_old_menu_locations['primary'] != $nav_menu_locations['primary'] ) {
302
+ $location_primary = 'Enabled';
303
+ }
304
+ } elseif ( empty( $this->_old_menu_locations['primary'] ) && isset( $nav_menu_locations['primary'] ) ) {
305
+ if ( $nav_menu_locations['primary'] == $menu_id ) {
306
+ $location_primary = 'Enabled';
307
+ }
308
+ } elseif ( isset( $this->_old_menu_locations['primary'] ) && empty( $nav_menu_locations['primary'] ) ) {
309
+ if ( $this->_old_menu_locations['primary'] == $menu_id ) {
310
+ $location_primary = 'Disabled';
311
+ }
312
+ }
313
+
314
+ // Alert 2082 Primary menu.
315
+ if ( ! empty( $location_primary ) ) {
316
+ $this->EventMenuSetting( $menu_data['menu-name'], $location_primary, 'Location: primary menu' );
317
+ }
318
+
319
+ $location_social = null;
320
+ if ( isset( $this->_old_menu_locations['social'] ) && isset( $nav_menu_locations['social'] ) ) {
321
+ if ( $nav_menu_locations['social'] == $menu_id && $this->_old_menu_locations['social'] != $nav_menu_locations['social'] ) {
322
+ $location_social = 'Enabled';
323
+ }
324
+ } elseif ( empty( $this->_old_menu_locations['social'] ) && isset( $nav_menu_locations['social'] ) ) {
325
+ if ( $nav_menu_locations['social'] == $menu_id ) {
326
+ $location_social = 'Enabled';
327
+ }
328
+ } elseif ( isset( $this->_old_menu_locations['social'] ) && empty( $nav_menu_locations['social'] ) ) {
329
+ if ( $this->_old_menu_locations['social'] == $menu_id ) {
330
+ $location_social = 'Disabled';
331
+ }
332
+ }
333
+
334
+ // Alert 2082 Social links menu.
335
+ if ( ! empty( $location_social ) ) {
336
+ $this->EventMenuSetting( $menu_data['menu-name'], $location_social, 'Location: social menu' );
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Set old menu terms and items.
343
+ */
344
+ private function BuildOldMenuTermsAndItems() {
345
+ $menus = wp_get_nav_menus();
346
+ if ( ! empty( $menus ) ) {
347
+ foreach ( $menus as $menu ) {
348
+ array_push(
349
+ $this->_old_menu_terms, array(
350
+ 'term_id' => $menu->term_id,
351
+ 'name' => $menu->name,
352
+ )
353
+ );
354
+ $items = wp_get_nav_menu_items( $menu->term_id );
355
+ if ( ! empty( $items ) ) {
356
+ foreach ( $items as $item ) {
357
+ array_push(
358
+ $this->_old_menu_items, array(
359
+ 'menu_id' => $menu->term_id,
360
+ 'item_id' => $item->ID,
361
+ 'title' => $item->title,
362
+ 'object' => $item->object,
363
+ 'menu_name' => $menu->name,
364
+ 'menu_order' => $item->menu_order,
365
+ 'url' => $item->url,
366
+ 'menu_item_parent' => $item->menu_item_parent,
367
+ )
368
+ );
369
+ }
370
+ }
371
+ }
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Triggered when a user accesses the admin area.
377
+ */
378
+ public function EventAdminInit() {
379
+ // Filter global arrays for security.
380
+ $server_array = filter_input_array( INPUT_SERVER );
381
+ $get_array = filter_input_array( INPUT_GET );
382
+
383
+ $is_nav_menu = basename( $server_array['SCRIPT_NAME'] ) == 'nav-menus.php';
384
+ if ( $is_nav_menu ) {
385
+ if ( isset( $get_array['action'] ) && 'delete' == $get_array['action'] ) {
386
+ if ( isset( $get_array['menu'] ) ) {
387
+ $this->_old_menu = wp_get_nav_menu_object( $get_array['menu'] );
388
+ }
389
+ } else {
390
+ $this->BuildOldMenuTermsAndItems();
391
+ }
392
+ $this->_old_menu_locations = get_nav_menu_locations();
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Customize set old data.
398
+ */
399
+ public function CustomizeInit() {
400
+ $this->BuildOldMenuTermsAndItems();
401
+ $this->_old_menu_locations = get_nav_menu_locations();
402
+ }
403
+
404
+ /**
405
+ * Customize Events Function.
406
+ */
407
+ public function CustomizeSave() {
408
+ $update_menus = array();
409
+ $menus = wp_get_nav_menus();
410
+ if ( ! empty( $menus ) ) {
411
+ foreach ( $menus as $menu ) {
412
+ array_push(
413
+ $update_menus, array(
414
+ 'term_id' => $menu->term_id,
415
+ 'name' => $menu->name,
416
+ )
417
+ );
418
+ }
419
+ }
420
+
421
+ // Deleted Menu.
422
+ if ( isset( $update_menus ) && isset( $this->_old_menu_terms ) ) {
423
+ $terms = array_diff( array_map( 'serialize', $this->_old_menu_terms ), array_map( 'serialize', $update_menus ) );
424
+ $terms = array_map( 'unserialize', $terms );
425
+
426
+ if ( isset( $terms ) && count( $terms ) > 0 ) {
427
+ foreach ( $terms as $term ) {
428
+ $this->plugin->alerts->Trigger(
429
+ 2081, array(
430
+ 'MenuName' => $term['name'],
431
+ )
432
+ );
433
+ }
434
+ }
435
+ }
436
+
437
+ // Filter $_POST global array for security.
438
+ $post_array = filter_input_array( INPUT_POST );
439
+
440
+ if ( isset( $post_array['action'] ) && 'customize_save' == $post_array['action'] ) {
441
+ if ( isset( $post_array['wp_customize'], $post_array['customized'] ) ) {
442
+ $customized = json_decode( wp_unslash( $post_array['customized'] ), true );
443
+ if ( is_array( $customized ) ) {
444
+ foreach ( $customized as $key => $value ) {
445
+ if ( ! empty( $value['nav_menu_term_id'] ) ) {
446
+ $is_occurred_event = false;
447
+ $menu = wp_get_nav_menu_object( $value['nav_menu_term_id'] );
448
+ $content_name = ! empty( $value['title'] ) ? $value['title'] : 'no title';
449
+ if ( ! empty( $this->_old_menu_items ) ) {
450
+ foreach ( $this->_old_menu_items as $old_item ) {
451
+ $item_id = substr( trim( $key, ']' ), 14 );
452
+ if ( $old_item['item_id'] == $item_id ) {
453
+ // Modified Items in the menu.
454
+ if ( $old_item['title'] != $content_name ) {
455
+ $is_occurred_event = true;
456
+ $this->EventModifiedItems( $value['type_label'], $content_name, $menu->name );
457
+ }
458
+ // Moved as a sub-item.
459
+ if ( $old_item['menu_item_parent'] != $value['menu_item_parent'] && 0 != $value['menu_item_parent'] ) {
460
+ $is_occurred_event = true;
461
+ $parent_name = $this->GetItemName( $value['nav_menu_term_id'], $value['menu_item_parent'] );
462
+ $this->EventChangeSubItem( $content_name, $parent_name, $menu->name );
463
+ }
464
+ // Changed order of the objects in a menu.
465
+ if ( $old_item['menu_order'] != $value['position'] ) {
466
+ $is_occurred_event = true;
467
+ $this->EventChangeOrder( $content_name, $menu->name );
468
+ }
469
+ }
470
+ }
471
+ }
472
+ // Add Items to the menu.
473
+ if ( ! $is_occurred_event ) {
474
+ $menu_name = ! empty( $customized['new_menu_name'] ) ? $customized['new_menu_name'] : $menu->name;
475
+ $this->EventAddItems( $value['type_label'], $content_name, $menu_name );
476
+ }
477
+ } else {
478
+ // Menu changed name.
479
+ if ( isset( $update_menus ) && isset( $this->_old_menu_terms ) ) {
480
+ foreach ( $this->_old_menu_terms as $old_menu ) {
481
+ foreach ( $update_menus as $update_menu ) {
482
+ if ( $old_menu['term_id'] == $update_menu['term_id'] && $old_menu['name'] != $update_menu['name'] ) {
483
+ $this->EventChangeName( $old_menu['name'], $update_menu['name'] );
484
+ }
485
+ }
486
+ }
487
+ }
488
+ // Setting Auto add pages.
489
+ if ( ! empty( $value ) && isset( $value['auto_add'] ) ) {
490
+ if ( $value['auto_add'] ) {
491
+ $this->EventMenuSetting( $value['name'], 'Enabled', 'Auto add pages' );
492
+ } else {
493
+ $this->EventMenuSetting( $value['name'], 'Disabled', 'Auto add pages' );
494
+ }
495
+ }
496
+ // Setting Location.
497
+ if ( false !== strpos( $key, 'nav_menu_locations[' ) ) {
498
+ $loc = substr( trim( $key, ']' ), 19 );
499
+ if ( ! empty( $value ) ) {
500
+ $menu = wp_get_nav_menu_object( $value );
501
+ $menu_name = ! empty( $customized['new_menu_name'] ) ? $customized['new_menu_name'] : ( ! empty( $menu ) ? $menu->name : '');
502
+ $this->EventMenuSetting( $menu_name, 'Enabled', 'Location: ' . $loc . ' menu' );
503
+ } else {
504
+ if ( ! empty( $this->_old_menu_locations[ $loc ] ) ) {
505
+ $menu = wp_get_nav_menu_object( $this->_old_menu_locations[ $loc ] );
506
+ $menu_name = ! empty( $customized['new_menu_name'] ) ? $customized['new_menu_name'] : ( ! empty( $menu ) ? $menu->name : '');
507
+ $this->EventMenuSetting( $menu_name, 'Disabled', 'Location: ' . $loc . ' menu' );
508
+ }
509
+ }
510
+ }
511
+ // Remove items from the menu.
512
+ if ( false !== strpos( $key, 'nav_menu_item[' ) ) {
513
+ $item_id = substr( trim( $key, ']' ), 14 );
514
+ if ( ! empty( $this->_old_menu_items ) ) {
515
+ foreach ( $this->_old_menu_items as $old_item ) {
516
+ if ( $old_item['item_id'] == $item_id ) {
517
+ $this->EventRemoveItems( $old_item['object'], $old_item['title'], $old_item['menu_name'] );
518
+ }
519
+ }
520
+ }
521
+ }
522
+ }
523
+ }
524
+ }
525
+ }
526
+ }
527
+ }
528
+
529
+ /**
530
+ * Added content to a menu.
531
+ *
532
+ * @param string $content_type - Type of content.
533
+ * @param string $content_name - Name of content.
534
+ * @param string $menu_name - Menu name.
535
+ */
536
+ private function EventAddItems( $content_type, $content_name, $menu_name ) {
537
+ $this->plugin->alerts->Trigger(
538
+ 2079, array(
539
+ 'ContentType' => $content_type,
540
+ 'ContentName' => $content_name,
541
+ 'MenuName' => $menu_name,
542
+ )
543
+ );
544
+ }
545
+
546
+ /**
547
+ * Removed content from a menu.
548
+ *
549
+ * @param string $content_type - Type of content.
550
+ * @param string $content_name - Name of content.
551
+ * @param string $menu_name - Menu name.
552
+ */
553
+ private function EventRemoveItems( $content_type, $content_name, $menu_name ) {
554
+ $this->plugin->alerts->Trigger(
555
+ 2080, array(
556
+ 'ContentType' => $content_type,
557
+ 'ContentName' => $content_name,
558
+ 'MenuName' => $menu_name,
559
+ )
560
+ );
561
+ }
562
+
563
+ /**
564
+ * Changed menu setting.
565
+ *
566
+ * @param string $menu_name - Menu Name.
567
+ * @param string $status - Status of menu.
568
+ * @param string $menu_setting - Menu setting.
569
+ */
570
+ private function EventMenuSetting( $menu_name, $status, $menu_setting ) {
571
+ $this->plugin->alerts->Trigger(
572
+ 2082, array(
573
+ 'Status' => $status,
574
+ 'MenuSetting' => $menu_setting,
575
+ 'MenuName' => $menu_name,
576
+ )
577
+ );
578
+ }
579
+
580
+ /**
581
+ * Modified content in a menu.
582
+ *
583
+ * @param string $content_type - Type of content.
584
+ * @param string $content_name - Name of content.
585
+ * @param string $menu_name - Menu name.
586
+ */
587
+ private function EventModifiedItems( $content_type, $content_name, $menu_name ) {
588
+ $this->plugin->alerts->Trigger(
589
+ 2083, array(
590
+ 'ContentType' => $content_type,
591
+ 'ContentName' => $content_name,
592
+ 'MenuName' => $menu_name,
593
+ )
594
+ );
595
+ }
596
+
597
+ /**
598
+ * Changed name of a menu.
599
+ *
600
+ * @param string $old_menu_name - Old Menu Name.
601
+ * @param string $new_menu_name - New Menu Name.
602
+ */
603
+ private function EventChangeName( $old_menu_name, $new_menu_name ) {
604
+ $this->plugin->alerts->Trigger(
605
+ 2084, array(
606
+ 'OldMenuName' => $old_menu_name,
607
+ 'NewMenuName' => $new_menu_name,
608
+ )
609
+ );
610
+ }
611
+
612
+ /**
613
+ * Changed order of the objects in a menu.
614
+ *
615
+ * @param string $item_name - Item name.
616
+ * @param string $menu_name - Menu name.
617
+ */
618
+ private function EventChangeOrder( $item_name, $menu_name ) {
619
+ $this->plugin->alerts->Trigger(
620
+ 2085, array(
621
+ 'ItemName' => $item_name,
622
+ 'MenuName' => $menu_name,
623
+ )
624
+ );
625
+ }
626
+
627
+ /**
628
+ * Moved objects as a sub-item.
629
+ *
630
+ * @param string $item_name - Item name.
631
+ * @param string $parent_name - Parent Name.
632
+ * @param string $menu_name - Menu Name.
633
+ */
634
+ private function EventChangeSubItem( $item_name, $parent_name, $menu_name ) {
635
+ $this->plugin->alerts->Trigger(
636
+ 2089, array(
637
+ 'ItemName' => $item_name,
638
+ 'ParentName' => $parent_name,
639
+ 'MenuName' => $menu_name,
640
+ )
641
+ );
642
+ }
643
+
644
+ /**
645
+ * Get menu item name.
646
+ *
647
+ * @param int $term_id - Term ID.
648
+ * @param int $item_id - Item ID.
649
+ */
650
+ private function GetItemName( $term_id, $item_id ) {
651
+ $item_name = '';
652
+ $menu_items = wp_get_nav_menu_items( $term_id );
653
+ foreach ( $menu_items as $menu_item ) {
654
+ if ( $menu_item->ID == $item_id ) {
655
+ $item_name = $menu_item->title;
656
+ break;
657
+ }
658
+ }
659
+ return $item_name;
660
+ }
661
  }
classes/Sensors/MetaData.php CHANGED
@@ -1,4 +1,18 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
  * Custom fields (posts, pages, custom posts and users) sensor.
4
  *
@@ -61,7 +75,7 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
61
  /**
62
  * Check "Excluded Custom Fields" or meta keys starts with "_".
63
  *
64
- * @param int $object_id - Object ID.
65
  * @param string $meta_key - Meta key.
66
  * @return boolean can log true|false
67
  */
@@ -84,14 +98,14 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
84
  * @return boolean is excluded from monitoring true|false
85
  */
86
  public function IsExcludedCustomFields( $custom ) {
87
- $customFields = $this->plugin->settings->GetExcludedMonitoringCustom();
88
- if ( in_array( $custom, $customFields ) ) {
89
  return true;
90
  }
91
- foreach ( $customFields as $field ) {
92
- if ( false !== strpos( $field, "*" ) ) {
93
- // wildcard str[any_character] when you enter (str*)
94
- if ( substr( $field, -1 ) == '*') {
95
  $field = rtrim( $field, '*' );
96
  if ( preg_match( "/^$field/", $custom ) ) {
97
  return true;
@@ -107,15 +121,15 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
107
  }
108
  }
109
  return false;
110
- // return (in_array($custom, $customFields)) ? true : false;
111
  }
112
 
113
  /**
114
  * Created a custom field.
115
  *
116
- * @param int $object_id - Object ID.
117
  * @param string $meta_key - Meta key.
118
- * @param mix $meta_value - Meta value.
119
  */
120
  public function EventPostMetaCreated( $object_id, $meta_key, $meta_value ) {
121
  $post = get_post( $object_id );
@@ -129,45 +143,61 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
129
 
130
  if ( empty( $meta_value ) && ( $this->null_meta_counter < 1 ) ) { // Report only one NULL meta value.
131
  $this->null_meta_counter += 1;
132
- } else { // Do not report if NULL meta values are more than one.
133
  return;
134
  }
135
 
 
 
 
 
 
 
 
 
 
 
136
  $wp_action = array( 'add-meta' );
137
 
138
- if ( isset( $_POST['action'] ) && ( 'editpost' == $_POST['action'] || in_array( $_POST['action'], $wp_action ) ) ) {
139
- $editorLink = $this->GetEditorLink( $post );
140
  switch ( $post->post_type ) {
141
  case 'page':
142
- $this->plugin->alerts->Trigger( 2059, array(
143
- 'PostID' => $object_id,
144
- 'PostTitle' => $post->post_title,
145
- 'MetaKey' => $meta_key,
146
- 'MetaValue' => $meta_value,
147
- 'MetaLink' => $meta_key,
148
- $editorLink['name'] => $editorLink['value'],
149
- ) );
 
 
150
  break;
151
  case 'post':
152
- $this->plugin->alerts->Trigger( 2053, array(
153
- 'PostID' => $object_id,
154
- 'PostTitle' => $post->post_title,
155
- 'MetaKey' => $meta_key,
156
- 'MetaValue' => $meta_value,
157
- 'MetaLink' => $meta_key,
158
- $editorLink['name'] => $editorLink['value'],
159
- ) );
 
 
160
  break;
161
  default:
162
- $this->plugin->alerts->Trigger( 2056, array(
163
- 'PostID' => $object_id,
164
- 'PostTitle' => $post->post_title,
165
- 'PostType' => $post->post_type,
166
- 'MetaKey' => $meta_key,
167
- 'MetaValue' => $meta_value,
168
- 'MetaLink' => $meta_key,
169
- $editorLink['name'] => $editorLink['value'],
170
- ) );
 
 
171
  break;
172
  }
173
  }
@@ -176,8 +206,8 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
176
  /**
177
  * Sets the old meta.
178
  *
179
- * @param int $meta_id - Meta ID.
180
- * @param int $object_id - Object ID.
181
  * @param string $meta_key - Meta key.
182
  */
183
  public function EventPostMetaUpdating( $meta_id, $object_id, $meta_key ) {
@@ -191,10 +221,10 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
191
  /**
192
  * Updated a custom field name/value.
193
  *
194
- * @param int $meta_id - Meta ID.
195
- * @param int $object_id - Object ID.
196
  * @param string $meta_key - Meta key.
197
- * @param mix $meta_value - Meta value.
198
  */
199
  public function EventPostMetaUpdated( $meta_id, $object_id, $meta_key, $meta_value ) {
200
  $post = get_post( $object_id );
@@ -206,90 +236,112 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
206
  return;
207
  }
208
 
 
 
 
 
 
 
 
 
 
 
209
  $wp_action = array( 'add-meta' );
210
 
211
- if ( isset( $_POST['action'] ) && ( 'editpost' == $_POST['action'] || in_array( $_POST['action'], $wp_action ) ) ) {
212
- $editorLink = $this->GetEditorLink( $post );
213
  if ( isset( $this->old_meta[ $meta_id ] ) ) {
214
  // Check change in meta key.
215
  if ( $this->old_meta[ $meta_id ]->key != $meta_key ) {
216
  switch ( $post->post_type ) {
217
  case 'page':
218
- $this->plugin->alerts->Trigger( 2064, array(
219
- 'PostID' => $object_id,
220
- 'PostTitle' => $post->post_title,
221
- 'MetaID' => $meta_id,
222
- 'MetaKeyNew' => $meta_key,
223
- 'MetaKeyOld' => $this->old_meta[$meta_id]->key,
224
- 'MetaValue' => $meta_value,
225
- 'MetaLink' => $meta_key,
226
- $editorLink['name'] => $editorLink['value'],
227
- ) );
 
 
228
  break;
229
  case 'post':
230
- $this->plugin->alerts->Trigger( 2062, array(
231
- 'PostID' => $object_id,
232
- 'PostTitle' => $post->post_title,
233
- 'MetaID' => $meta_id,
234
- 'MetaKeyNew' => $meta_key,
235
- 'MetaKeyOld' => $this->old_meta[$meta_id]->key,
236
- 'MetaValue' => $meta_value,
237
- 'MetaLink' => $meta_key,
238
- $editorLink['name'] => $editorLink['value'],
239
- ) );
 
 
240
  break;
241
  default:
242
- $this->plugin->alerts->Trigger( 2063, array(
243
- 'PostID' => $object_id,
244
- 'PostTitle' => $post->post_title,
245
- 'PostType' => $post->post_type,
246
- 'MetaID' => $meta_id,
247
- 'MetaKeyNew' => $meta_key,
248
- 'MetaKeyOld' => $this->old_meta[$meta_id]->key,
249
- 'MetaValue' => $meta_value,
250
- 'MetaLink' => $smeta_key,
251
- $editorLink['name'] => $editorLink['value'],
252
- ) );
 
 
253
  break;
254
  }
255
  } elseif ( $this->old_meta[ $meta_id ]->val != $meta_value ) { // Check change in meta value.
256
  switch ( $post->post_type ) {
257
  case 'page':
258
- $this->plugin->alerts->Trigger( 2060, array(
259
- 'PostID' => $object_id,
260
- 'PostTitle' => $post->post_title,
261
- 'MetaID' => $meta_id,
262
- 'MetaKey' => $meta_key,
263
- 'MetaValueNew' => $meta_value,
264
- 'MetaValueOld' => $this->old_meta[$meta_id]->val,
265
- 'MetaLink' => $meta_key,
266
- $editorLink['name'] => $editorLink['value'],
267
- ) );
 
 
268
  break;
269
  case 'post':
270
- $this->plugin->alerts->Trigger( 2054, array(
271
- 'PostID' => $object_id,
272
- 'PostTitle' => $post->post_title,
273
- 'MetaID' => $meta_id,
274
- 'MetaKey' => $meta_key,
275
- 'MetaValueNew' => $meta_value,
276
- 'MetaValueOld' => $this->old_meta[$meta_id]->val,
277
- 'MetaLink' => $meta_key,
278
- $editorLink['name'] => $editorLink['value'],
279
- ) );
 
 
280
  break;
281
  default:
282
- $this->plugin->alerts->Trigger( 2057, array(
283
- 'PostID' => $object_id,
284
- 'PostTitle' => $post->post_title,
285
- 'PostType' => $post->post_type,
286
- 'MetaID' => $meta_id,
287
- 'MetaKey' => $meta_key,
288
- 'MetaValueNew' => $meta_value,
289
- 'MetaValueOld' => $this->old_meta[$meta_id]->val,
290
- 'MetaLink' => $meta_key,
291
- $editorLink['name'] => $editorLink['value'],
292
- ) );
 
 
293
  break;
294
  }
295
  }
@@ -302,10 +354,10 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
302
  /**
303
  * Deleted a custom field.
304
  *
305
- * @param int $meta_ids - Meta IDs.
306
- * @param int $object_id - Object ID.
307
  * @param string $meta_key - Meta key.
308
- * @param mix $meta_value - Meta value.
309
  */
310
  public function EventPostMetaDeleted( $meta_ids, $object_id, $meta_key, $meta_value ) {
311
 
@@ -316,45 +368,61 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
316
 
317
  $post = get_post( $object_id );
318
 
 
 
 
 
 
 
 
 
 
 
319
  $wp_action = array( 'delete-meta' );
320
 
321
- if ( isset( $_POST['action'] ) && in_array( $_POST['action'], $wp_action ) ) {
322
- $editorLink = $this->GetEditorLink( $post );
323
  foreach ( $meta_ids as $meta_id ) {
324
  if ( ! $this->CanLogMetaKey( $object_id, $meta_key ) ) {
325
  continue;
326
  }
327
  switch ( $post->post_type ) {
328
  case 'page':
329
- $this->plugin->alerts->Trigger( 2061, array(
330
- 'PostID' => $object_id,
331
- 'PostTitle' => $post->post_title,
332
- 'MetaID' => $meta_id,
333
- 'MetaKey' => $meta_key,
334
- 'MetaValue' => $meta_value,
335
- $editorLink['name'] => $editorLink['value'],
336
- ) );
 
 
337
  break;
338
  case 'post':
339
- $this->plugin->alerts->Trigger( 2055, array(
340
- 'PostID' => $object_id,
341
- 'PostTitle' => $post->post_title,
342
- 'MetaID' => $meta_id,
343
- 'MetaKey' => $meta_key,
344
- 'MetaValue' => $meta_value,
345
- $editorLink['name'] => $editorLink['value'],
346
- ) );
 
 
347
  break;
348
  default:
349
- $this->plugin->alerts->Trigger( 2058, array(
350
- 'PostID' => $object_id,
351
- 'PostTitle' => $post->post_title,
352
- 'PostType' => $post->post_type,
353
- 'MetaID' => $meta_id,
354
- 'MetaKey' => $meta_key,
355
- 'MetaValue' => $meta_value,
356
- $editorLink['name'] => $editorLink['value'],
357
- ) );
 
 
358
  break;
359
  }
360
  }
@@ -373,26 +441,26 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
373
  /**
374
  * Get editor link.
375
  *
376
- * @param stdClass $post the post.
377
- * @return array $aLink name and value link
378
  */
379
  private function GetEditorLink( $post ) {
380
  $name = 'EditorLink';
381
  $name .= ( 'page' == $post->post_type ) ? 'Page' : 'Post';
382
  $value = get_edit_post_link( $post->ID );
383
- $aLink = array(
384
  'name' => $name,
385
  'value' => $value,
386
  );
387
- return $aLink;
388
  }
389
 
390
  /**
391
  * Create a custom field name/value.
392
  *
393
- * @param int $object_id - Object ID.
394
  * @param string $meta_key - Meta key.
395
- * @param mix $meta_value - Meta value.
396
  */
397
  public function event_user_meta_created( $object_id, $meta_key, $meta_value ) {
398
 
@@ -406,28 +474,35 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
406
 
407
  if ( empty( $meta_value ) && ( $this->null_meta_counter < 1 ) ) { // Report only one NULL meta value.
408
  $this->null_meta_counter += 1;
409
- } else { // Do not report if NULL meta values are more than one.
410
  return;
411
  }
412
 
413
- // Get POST array.
414
- $post_array = $_POST;
 
 
 
 
 
415
 
416
  // If update action is set then trigger the alert.
417
  if ( isset( $post_array['action'] ) && ( 'update' == $post_array['action'] || 'createuser' == $post_array['action'] ) ) {
418
- $this->plugin->alerts->Trigger( 4016, array(
419
- 'TargetUsername' => $user->user_login,
420
- 'custom_field_name' => $meta_key,
421
- 'new_value' => $meta_value,
422
- ) );
 
 
423
  }
424
  }
425
 
426
  /**
427
  * Sets the old meta.
428
  *
429
- * @param int $meta_id - Meta ID.
430
- * @param int $object_id - Object ID.
431
  * @param string $meta_key - Meta key.
432
  */
433
  public function event_user_meta_updating( $meta_id, $object_id, $meta_key ) {
@@ -459,20 +534,27 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
459
  // User profile name related meta.
460
  $username_meta = array( 'first_name', 'last_name', 'nickname' );
461
 
462
- // Get POST array.
463
- $post_array = $_POST;
 
 
 
 
 
464
 
465
  // If update action is set then trigger the alert.
466
  if ( isset( $post_array['action'] ) && 'update' == $post_array['action'] ) {
467
  if ( isset( $this->old_meta[ $meta_id ] ) && ! in_array( $meta_key, $username_meta, true ) ) {
468
  // Check change in meta value.
469
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
470
- $this->plugin->alerts->Trigger( 4015, array(
471
- 'TargetUsername' => $user->user_login,
472
- 'custom_field_name' => $meta_key,
473
- 'new_value' => $meta_value,
474
- 'old_value' => $this->old_meta[ $meta_id ]->val,
475
- ) );
 
 
476
  }
477
  // Remove old meta update data.
478
  unset( $this->old_meta[ $meta_id ] );
@@ -481,31 +563,37 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
481
  switch ( $meta_key ) {
482
  case 'first_name':
483
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
484
- $this->plugin->alerts->Trigger( 4017, array(
485
- 'TargetUsername' => $user->user_login,
486
- 'new_firstname' => $meta_value,
487
- 'old_firstname' => $this->old_meta[ $meta_id ]->val,
488
- ) );
 
 
489
  }
490
  break;
491
 
492
  case 'last_name':
493
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
494
- $this->plugin->alerts->Trigger( 4018, array(
495
- 'TargetUsername' => $user->user_login,
496
- 'new_lastname' => $meta_value,
497
- 'old_lastname' => $this->old_meta[ $meta_id ]->val,
498
- ) );
 
 
499
  }
500
  break;
501
 
502
  case 'nickname':
503
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
504
- $this->plugin->alerts->Trigger( 4019, array(
505
- 'TargetUsername' => $user->user_login,
506
- 'new_nickname' => $meta_value,
507
- 'old_nickname' => $this->old_meta[ $meta_id ]->val,
508
- ) );
 
 
509
  }
510
  break;
511
 
@@ -534,11 +622,13 @@ class WSAL_Sensors_MetaData extends WSAL_AbstractSensor {
534
 
535
  // Alert if display name is changed.
536
  if ( $old_display_name !== $new_display_name ) {
537
- $this->plugin->alerts->Trigger( 4020, array(
538
- 'TargetUsername' => $new_userdata->user_login,
539
- 'new_displayname' => $new_display_name,
540
- 'old_displayname' => $old_display_name,
541
- ) );
 
 
542
  }
543
 
544
  }
1
  <?php
2
+ /**
3
+ * Sensor: Meta Data
4
+ *
5
+ * Meta Data sensor file.
6
+ *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
  /**
17
  * Custom fields (posts, pages, custom posts and users) sensor.
18
  *
75
  /**
76
  * Check "Excluded Custom Fields" or meta keys starts with "_".
77
  *
78
+ * @param int $object_id - Object ID.
79
  * @param string $meta_key - Meta key.
80
  * @return boolean can log true|false
81
  */
98
  * @return boolean is excluded from monitoring true|false
99
  */
100
  public function IsExcludedCustomFields( $custom ) {
101
+ $custom_fields = $this->plugin->settings->GetExcludedMonitoringCustom();
102
+ if ( in_array( $custom, $custom_fields ) ) {
103
  return true;
104
  }
105
+ foreach ( $custom_fields as $field ) {
106
+ if ( false !== strpos( $field, '*' ) ) {
107
+ // Wildcard str[any_character] when you enter (str*).
108
+ if ( substr( $field, -1 ) == '*' ) {
109
  $field = rtrim( $field, '*' );
110
  if ( preg_match( "/^$field/", $custom ) ) {
111
  return true;
121
  }
122
  }
123
  return false;
124
+ // return (in_array($custom, $custom_fields)) ? true : false;.
125
  }
126
 
127
  /**
128
  * Created a custom field.
129
  *
130
+ * @param int $object_id - Object ID.
131
  * @param string $meta_key - Meta key.
132
+ * @param mix $meta_value - Meta value.
133
  */
134
  public function EventPostMetaCreated( $object_id, $meta_key, $meta_value ) {
135
  $post = get_post( $object_id );
143
 
144
  if ( empty( $meta_value ) && ( $this->null_meta_counter < 1 ) ) { // Report only one NULL meta value.
145
  $this->null_meta_counter += 1;
146
+ } elseif ( $this->null_meta_counter >= 1 ) { // Do not report if NULL meta values are more than one.
147
  return;
148
  }
149
 
150
+ // Filter $_POST global array for security.
151
+ $post_array = filter_input_array( INPUT_POST );
152
+
153
+ // Check nonce.
154
+ if ( isset( $post_array['_ajax_nonce-add-meta'] ) && ! wp_verify_nonce( $post_array['_ajax_nonce-add-meta'], 'add-meta' ) ) {
155
+ return false;
156
+ } elseif ( isset( $post_array['_wpnonce'] ) && isset( $post_array['post_ID'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
157
+ return false;
158
+ }
159
+
160
  $wp_action = array( 'add-meta' );
161
 
162
+ if ( isset( $post_array['action'] ) && ( 'editpost' == $post_array['action'] || in_array( $post_array['action'], $wp_action ) ) ) {
163
+ $editor_link = $this->GetEditorLink( $post );
164
  switch ( $post->post_type ) {
165
  case 'page':
166
+ $this->plugin->alerts->Trigger(
167
+ 2059, array(
168
+ 'PostID' => $object_id,
169
+ 'PostTitle' => $post->post_title,
170
+ 'MetaKey' => $meta_key,
171
+ 'MetaValue' => $meta_value,
172
+ 'MetaLink' => $meta_key,
173
+ $editor_link['name'] => $editor_link['value'],
174
+ )
175
+ );
176
  break;
177
  case 'post':
178
+ $this->plugin->alerts->Trigger(
179
+ 2053, array(
180
+ 'PostID' => $object_id,
181
+ 'PostTitle' => $post->post_title,
182
+ 'MetaKey' => $meta_key,
183
+ 'MetaValue' => $meta_value,
184
+ 'MetaLink' => $meta_key,
185
+ $editor_link['name'] => $editor_link['value'],
186
+ )
187
+ );
188
  break;
189
  default:
190
+ $this->plugin->alerts->Trigger(
191
+ 2056, array(
192
+ 'PostID' => $object_id,
193
+ 'PostTitle' => $post->post_title,
194
+ 'PostType' => $post->post_type,
195
+ 'MetaKey' => $meta_key,
196
+ 'MetaValue' => $meta_value,
197
+ 'MetaLink' => $meta_key,
198
+ $editor_link['name'] => $editor_link['value'],
199
+ )
200
+ );
201
  break;
202
  }
203
  }
206
  /**
207
  * Sets the old meta.
208
  *
209
+ * @param int $meta_id - Meta ID.
210
+ * @param int $object_id - Object ID.
211
  * @param string $meta_key - Meta key.
212
  */
213
  public function EventPostMetaUpdating( $meta_id, $object_id, $meta_key ) {
221
  /**
222
  * Updated a custom field name/value.
223
  *
224
+ * @param int $meta_id - Meta ID.
225
+ * @param int $object_id - Object ID.
226
  * @param string $meta_key - Meta key.
227
+ * @param mix $meta_value - Meta value.
228
  */
229
  public function EventPostMetaUpdated( $meta_id, $object_id, $meta_key, $meta_value ) {
230
  $post = get_post( $object_id );
236
  return;
237
  }
238
 
239
+ // Filter $_POST global array for security.
240
+ $post_array = filter_input_array( INPUT_POST );
241
+
242
+ // Check nonce.
243
+ if ( isset( $post_array['_ajax_nonce'] ) && ! wp_verify_nonce( $post_array['_ajax_nonce'], 'change-meta' ) ) {
244
+ return false;
245
+ } elseif ( isset( $post_array['_wpnonce'] ) && isset( $post_array['post_ID'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
246
+ return false;
247
+ }
248
+
249
  $wp_action = array( 'add-meta' );
250
 
251
+ if ( isset( $post_array['action'] ) && ( 'editpost' == $post_array['action'] || in_array( $post_array['action'], $wp_action ) ) ) {
252
+ $editor_link = $this->GetEditorLink( $post );
253
  if ( isset( $this->old_meta[ $meta_id ] ) ) {
254
  // Check change in meta key.
255
  if ( $this->old_meta[ $meta_id ]->key != $meta_key ) {
256
  switch ( $post->post_type ) {
257
  case 'page':
258
+ $this->plugin->alerts->Trigger(
259
+ 2064, array(
260
+ 'PostID' => $object_id,
261
+ 'PostTitle' => $post->post_title,
262
+ 'MetaID' => $meta_id,
263
+ 'MetaKeyNew' => $meta_key,
264
+ 'MetaKeyOld' => $this->old_meta[ $meta_id ]->key,
265
+ 'MetaValue' => $meta_value,
266
+ 'MetaLink' => $meta_key,
267
+ $editor_link['name'] => $editor_link['value'],
268
+ )
269
+ );
270
  break;
271
  case 'post':
272
+ $this->plugin->alerts->Trigger(
273
+ 2062, array(
274
+ 'PostID' => $object_id,
275
+ 'PostTitle' => $post->post_title,
276
+ 'MetaID' => $meta_id,
277
+ 'MetaKeyNew' => $meta_key,
278
+ 'MetaKeyOld' => $this->old_meta[ $meta_id ]->key,
279
+ 'MetaValue' => $meta_value,
280
+ 'MetaLink' => $meta_key,
281
+ $editor_link['name'] => $editor_link['value'],
282
+ )
283
+ );
284
  break;
285
  default:
286
+ $this->plugin->alerts->Trigger(
287
+ 2063, array(
288
+ 'PostID' => $object_id,
289
+ 'PostTitle' => $post->post_title,
290
+ 'PostType' => $post->post_type,
291
+ 'MetaID' => $meta_id,
292
+ 'MetaKeyNew' => $meta_key,
293
+ 'MetaKeyOld' => $this->old_meta[ $meta_id ]->key,
294
+ 'MetaValue' => $meta_value,
295
+ 'MetaLink' => $smeta_key,
296
+ $editor_link['name'] => $editor_link['value'],
297
+ )
298
+ );
299
  break;
300
  }
301
  } elseif ( $this->old_meta[ $meta_id ]->val != $meta_value ) { // Check change in meta value.
302
  switch ( $post->post_type ) {
303
  case 'page':
304
+ $this->plugin->alerts->Trigger(
305
+ 2060, array(
306
+ 'PostID' => $object_id,
307
+ 'PostTitle' => $post->post_title,
308
+ 'MetaID' => $meta_id,
309
+ 'MetaKey' => $meta_key,
310
+ 'MetaValueNew' => $meta_value,
311
+ 'MetaValueOld' => $this->old_meta[ $meta_id ]->val,
312
+ 'MetaLink' => $meta_key,
313
+ $editor_link['name'] => $editor_link['value'],
314
+ )
315
+ );
316
  break;
317
  case 'post':
318
+ $this->plugin->alerts->Trigger(
319
+ 2054, array(
320
+ 'PostID' => $object_id,
321
+ 'PostTitle' => $post->post_title,
322
+ 'MetaID' => $meta_id,
323
+ 'MetaKey' => $meta_key,
324
+ 'MetaValueNew' => $meta_value,
325
+ 'MetaValueOld' => $this->old_meta[ $meta_id ]->val,
326
+ 'MetaLink' => $meta_key,
327
+ $editor_link['name'] => $editor_link['value'],
328
+ )
329
+ );
330
  break;
331
  default:
332
+ $this->plugin->alerts->Trigger(
333
+ 2057, array(
334
+ 'PostID' => $object_id,
335
+ 'PostTitle' => $post->post_title,
336
+ 'PostType' => $post->post_type,
337
+ 'MetaID' => $meta_id,
338
+ 'MetaKey' => $meta_key,
339
+ 'MetaValueNew' => $meta_value,
340
+ 'MetaValueOld' => $this->old_meta[ $meta_id ]->val,
341
+ 'MetaLink' => $meta_key,
342
+ $editor_link['name'] => $editor_link['value'],
343
+ )
344
+ );
345
  break;
346
  }
347
  }
354
  /**
355
  * Deleted a custom field.
356
  *
357
+ * @param int $meta_ids - Meta IDs.
358
+ * @param int $object_id - Object ID.
359
  * @param string $meta_key - Meta key.
360
+ * @param mix $meta_value - Meta value.
361
  */
362
  public function EventPostMetaDeleted( $meta_ids, $object_id, $meta_key, $meta_value ) {
363
 
368
 
369
  $post = get_post( $object_id );
370
 
371
+ // Filter $_POST global array for security.
372
+ $post_array = filter_input_array( INPUT_POST );
373
+
374
+ // Check nonce.
375
+ if ( isset( $post_array['_ajax_nonce'] ) && ! wp_verify_nonce( $post_array['_ajax_nonce'], 'delete-meta_' . $post_array['id'] ) ) {
376
+ return false;
377
+ } elseif ( isset( $post_array['_wpnonce'] ) && isset( $post_array['post_ID'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
378
+ return false;
379
+ }
380
+
381
  $wp_action = array( 'delete-meta' );
382
 
383
+ if ( isset( $post_array['action'] ) && in_array( $post_array['action'], $wp_action ) ) {
384
+ $editor_link = $this->GetEditorLink( $post );
385
  foreach ( $meta_ids as $meta_id ) {
386
  if ( ! $this->CanLogMetaKey( $object_id, $meta_key ) ) {
387
  continue;
388
  }
389
  switch ( $post->post_type ) {
390
  case 'page':
391
+ $this->plugin->alerts->Trigger(
392
+ 2061, array(
393
+ 'PostID' => $object_id,
394
+ 'PostTitle' => $post->post_title,
395
+ 'MetaID' => $meta_id,
396
+ 'MetaKey' => $meta_key,
397
+ 'MetaValue' => $meta_value,
398
+ $editor_link['name'] => $editor_link['value'],
399
+ )
400
+ );
401
  break;
402
  case 'post':
403
+ $this->plugin->alerts->Trigger(
404
+ 2055, array(
405
+ 'PostID' => $object_id,
406
+ 'PostTitle' => $post->post_title,
407
+ 'MetaID' => $meta_id,
408
+ 'MetaKey' => $meta_key,
409
+ 'MetaValue' => $meta_value,
410
+ $editor_link['name'] => $editor_link['value'],
411
+ )
412
+ );
413
  break;
414
  default:
415
+ $this->plugin->alerts->Trigger(
416
+ 2058, array(
417
+ 'PostID' => $object_id,
418
+ 'PostTitle' => $post->post_title,
419
+ 'PostType' => $post->post_type,
420
+ 'MetaID' => $meta_id,
421
+ 'MetaKey' => $meta_key,
422
+ 'MetaValue' => $meta_value,
423
+ $editor_link['name'] => $editor_link['value'],
424
+ )
425
+ );
426
  break;
427
  }
428
  }
441
  /**
442
  * Get editor link.
443
  *
444
+ * @param stdClass $post - The post.
445
+ * @return array $editor_link - Name and value link
446
  */
447
  private function GetEditorLink( $post ) {
448
  $name = 'EditorLink';
449
  $name .= ( 'page' == $post->post_type ) ? 'Page' : 'Post';
450
  $value = get_edit_post_link( $post->ID );
451
+ $editor_link = array(
452
  'name' => $name,
453
  'value' => $value,
454
  );
455
+ return $editor_link;
456
  }
457
 
458
  /**
459
  * Create a custom field name/value.
460
  *
461
+ * @param int $object_id - Object ID.
462
  * @param string $meta_key - Meta key.
463
+ * @param mix $meta_value - Meta value.
464
  */
465
  public function event_user_meta_created( $object_id, $meta_key, $meta_value ) {
466
 
474
 
475
  if ( empty( $meta_value ) && ( $this->null_meta_counter < 1 ) ) { // Report only one NULL meta value.
476
  $this->null_meta_counter += 1;
477
+ } elseif ( $this->null_meta_counter >= 1 ) { // Do not report if NULL meta values are more than one.
478
  return;
479
  }
480
 
481
+ // Filter $_POST global array for security.
482
+ $post_array = filter_input_array( INPUT_POST );
483
+
484
+ // Check nonce.
485
+ if ( isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-user_' . $user->ID ) ) {
486
+ return false;
487
+ }
488
 
489
  // If update action is set then trigger the alert.
490
  if ( isset( $post_array['action'] ) && ( 'update' == $post_array['action'] || 'createuser' == $post_array['action'] ) ) {
491
+ $this->plugin->alerts->Trigger(
492
+ 4016, array(
493
+ 'TargetUsername' => $user->user_login,
494
+ 'custom_field_name' => $meta_key,
495
+ 'new_value' => $meta_value,
496
+ )
497
+ );
498
  }
499
  }
500
 
501
  /**
502
  * Sets the old meta.
503
  *
504
+ * @param int $meta_id - Meta ID.
505
+ * @param int $object_id - Object ID.
506
  * @param string $meta_key - Meta key.
507
  */
508
  public function event_user_meta_updating( $meta_id, $object_id, $meta_key ) {
534
  // User profile name related meta.
535
  $username_meta = array( 'first_name', 'last_name', 'nickname' );
536
 
537
+ // Filter $_POST global array for security.
538
+ $post_array = filter_input_array( INPUT_POST );
539
+
540
+ // Check nonce.
541
+ if ( isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-user_' . $user->ID ) ) {
542
+ return false;
543
+ }
544
 
545
  // If update action is set then trigger the alert.
546
  if ( isset( $post_array['action'] ) && 'update' == $post_array['action'] ) {
547
  if ( isset( $this->old_meta[ $meta_id ] ) && ! in_array( $meta_key, $username_meta, true ) ) {
548
  // Check change in meta value.
549
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
550
+ $this->plugin->alerts->Trigger(
551
+ 4015, array(
552
+ 'TargetUsername' => $user->user_login,
553
+ 'custom_field_name' => $meta_key,
554
+ 'new_value' => $meta_value,
555
+ 'old_value' => $this->old_meta[ $meta_id ]->val,
556
+ )
557
+ );
558
  }
559
  // Remove old meta update data.
560
  unset( $this->old_meta[ $meta_id ] );
563
  switch ( $meta_key ) {
564
  case 'first_name':
565
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
566
+ $this->plugin->alerts->Trigger(
567
+ 4017, array(
568
+ 'TargetUsername' => $user->user_login,
569
+ 'new_firstname' => $meta_value,
570
+ 'old_firstname' => $this->old_meta[ $meta_id ]->val,
571
+ )
572
+ );
573
  }
574
  break;
575
 
576
  case 'last_name':
577
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
578
+ $this->plugin->alerts->Trigger(
579
+ 4018, array(
580
+ 'TargetUsername' => $user->user_login,
581
+ 'new_lastname' => $meta_value,
582
+ 'old_lastname' => $this->old_meta[ $meta_id ]->val,
583
+ )
584
+ );
585
  }
586
  break;
587
 
588
  case 'nickname':
589
  if ( $this->old_meta[ $meta_id ]->val != $meta_value ) {
590
+ $this->plugin->alerts->Trigger(
591
+ 4019, array(
592
+ 'TargetUsername' => $user->user_login,
593
+ 'new_nickname' => $meta_value,
594
+ 'old_nickname' => $this->old_meta[ $meta_id ]->val,
595
+ )
596
+ );
597
  }
598
  break;
599
 
622
 
623
  // Alert if display name is changed.
624
  if ( $old_display_name !== $new_display_name ) {
625
+ $this->plugin->alerts->Trigger(
626
+ 4020, array(
627
+ 'TargetUsername' => $new_userdata->user_login,
628
+ 'new_displayname' => $new_display_name,
629
+ 'old_displayname' => $old_display_name,
630
+ )
631
+ );
632
  }
633
 
634
  }
classes/Sensors/Multisite.php CHANGED
@@ -1,8 +1,20 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
5
- * Multisite sensor.
 
 
 
 
 
 
 
6
  *
7
  * 4010 Existing user added to a site
8
  * 4011 User removed from site
@@ -15,186 +27,223 @@
15
  * 7005 Existing site deleted from network
16
  * 5008 Activated theme on network
17
  * 5009 Deactivated theme from network
 
 
 
18
  */
19
- class WSAL_Sensors_Multisite extends WSAL_AbstractSensor
20
- {
21
- protected $old_allowedthemes = null;
22
-
23
- /**
24
- * Listening to events using WP hooks.
25
- */
26
- public function HookEvents()
27
- {
28
- if ($this->plugin->IsMultisite()) {
29
- add_action('admin_init', array($this, 'EventAdminInit'));
30
- if (current_user_can('switch_themes')) {
31
- add_action('shutdown', array($this, 'EventAdminShutdown'));
32
- }
33
- add_action('wpmu_new_blog', array($this, 'EventNewBlog'), 10, 1);
34
- add_action('archive_blog', array($this, 'EventArchiveBlog'));
35
- add_action('unarchive_blog', array($this, 'EventUnarchiveBlog'));
36
- add_action('activate_blog', array($this, 'EventActivateBlog'));
37
- add_action('deactivate_blog', array($this, 'EventDeactivateBlog'));
38
- add_action('delete_blog', array($this, 'EventDeleteBlog'));
39
- add_action('add_user_to_blog', array($this, 'EventUserAddedToBlog'), 10, 3);
40
- add_action('remove_user_from_blog', array($this, 'EventUserRemovedFromBlog'));
41
- }
42
- }
43
-
44
- /**
45
- * Triggered when a user accesses the admin area.
46
- */
47
- public function EventAdminInit()
48
- {
49
- $this->old_allowedthemes = array_keys((array)get_site_option('allowedthemes'));
50
- }
51
-
52
- /**
53
- * Activated/Deactivated theme on network.
54
- */
55
- public function EventAdminShutdown()
56
- {
57
- if (is_null($this->old_allowedthemes)) {
58
- return;
59
- }
60
- $new_allowedthemes = array_keys((array)get_site_option('allowedthemes'));
61
-
62
- // check for enabled themes
63
- foreach ($new_allowedthemes as $theme) {
64
- if (!in_array($theme, (array)$this->old_allowedthemes)) {
65
- $theme = wp_get_theme($theme);
66
- $this->plugin->alerts->Trigger(5008, array(
67
- 'Theme' => (object)array(
68
- 'Name' => $theme->Name,
69
- 'ThemeURI' => $theme->ThemeURI,
70
- 'Description' => $theme->Description,
71
- 'Author' => $theme->Author,
72
- 'Version' => $theme->Version,
73
- 'get_template_directory' => $theme->get_template_directory(),
74
- ),
75
- ));
76
- }
77
- }
78
-
79
- // check for disabled themes
80
- foreach ((array)$this->old_allowedthemes as $theme) {
81
- if (!in_array($theme, $new_allowedthemes)) {
82
- $theme = wp_get_theme($theme);
83
- $this->plugin->alerts->Trigger(5009, array(
84
- 'Theme' => (object)array(
85
- 'Name' => $theme->Name,
86
- 'ThemeURI' => $theme->ThemeURI,
87
- 'Description' => $theme->Description,
88
- 'Author' => $theme->Author,
89
- 'Version' => $theme->Version,
90
- 'get_template_directory' => $theme->get_template_directory(),
91
- ),
92
- ));
93
- }
94
- }
95
- }
96
-
97
- /**
98
- * New site added on the network.
99
- */
100
- public function EventNewBlog($blog_id)
101
- {
102
- $this->plugin->alerts->Trigger(7000, array(
103
- 'BlogID' => $blog_id,
104
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
105
- ));
106
- }
107
-
108
- /**
109
- * Existing site archived.
110
- */
111
- public function EventArchiveBlog($blog_id)
112
- {
113
- $this->plugin->alerts->Trigger(7001, array(
114
- 'BlogID' => $blog_id,
115
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
116
- ));
117
- }
118
-
119
- /**
120
- * Archived site has been unarchived.
121
- */
122
- public function EventUnarchiveBlog($blog_id)
123
- {
124
- $this->plugin->alerts->Trigger(7002, array(
125
- 'BlogID' => $blog_id,
126
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
127
- ));
128
- }
129
-
130
- /**
131
- * Deactivated site has been activated.
132
- */
133
- public function EventActivateBlog($blog_id)
134
- {
135
- $this->plugin->alerts->Trigger(7003, array(
136
- 'BlogID' => $blog_id,
137
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
138
- ));
139
- }
140
-
141
- /**
142
- * Site has been deactivated.
143
- */
144
- public function EventDeactivateBlog($blog_id)
145
- {
146
- $this->plugin->alerts->Trigger(7004, array(
147
- 'BlogID' => $blog_id,
148
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
149
- ));
150
- }
151
-
152
- /**
153
- * Existing site deleted from network.
154
- */
155
- public function EventDeleteBlog($blog_id)
156
- {
157
- $this->plugin->alerts->Trigger(7005, array(
158
- 'BlogID' => $blog_id,
159
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
160
- ));
161
- }
162
-
163
- /**
164
- * Existing user added to a site.
165
- */
166
- public function EventUserAddedToBlog($user_id, $role, $blog_id)
167
- {
168
- $this->plugin->alerts->TriggerIf(4010, array(
169
- 'TargetUserID' => $user_id,
170
- 'TargetUsername' => get_userdata($user_id)->user_login,
171
- 'TargetUserRole' => $role,
172
- 'BlogID' => $blog_id,
173
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
174
- ), array($this, 'MustNotContainCreateUser'));
175
- }
176
-
177
- /**
178
- * User removed from site.
179
- */
180
- public function EventUserRemovedFromBlog($user_id)
181
- {
182
- $user = get_userdata($user_id);
183
- $blog_id = (isset($_REQUEST['id']) ? $_REQUEST['id'] : 0);
184
- $this->plugin->alerts->TriggerIf(4011, array(
185
- 'TargetUserID' => $user_id,
186
- 'TargetUsername' => $user->user_login,
187
- 'TargetUserRole' => is_array($user->roles) ? implode(', ', $user->roles) : $user->roles,
188
- 'BlogID' => $blog_id,
189
- 'SiteName' => get_blog_option($blog_id, 'blogname'),
190
- ), array($this, 'MustNotContainCreateUser'));
191
- }
192
-
193
- /**
194
- * New network user created.
195
- */
196
- public function MustNotContainCreateUser(WSAL_AlertManager $mgr)
197
- {
198
- return !$mgr->WillTrigger(4012);
199
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  }
1
  <?php
2
  /**
3
+ * Sensor: Multisite
4
+ *
5
+ * Multisite sensor file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * Multisite Sensor.
18
  *
19
  * 4010 Existing user added to a site
20
  * 4011 User removed from site
27
  * 7005 Existing site deleted from network
28
  * 5008 Activated theme on network
29
  * 5009 Deactivated theme from network
30
+ *
31
+ * @package Wsal
32
+ * @subpackage Sensors
33
  */
34
+ class WSAL_Sensors_Multisite extends WSAL_AbstractSensor {
35
+
36
+ /**
37
+ * Allowed Themes.
38
+ *
39
+ * @var array
40
+ */
41
+ protected $old_allowedthemes = null;
42
+
43
+ /**
44
+ * Listening to events using WP hooks.
45
+ */
46
+ public function HookEvents() {
47
+ if ( $this->plugin->IsMultisite() ) {
48
+ add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
49
+ if ( current_user_can( 'switch_themes' ) ) {
50
+ add_action( 'shutdown', array( $this, 'EventAdminShutdown' ) );
51
+ }
52
+ add_action( 'wpmu_new_blog', array( $this, 'EventNewBlog' ), 10, 1 );
53
+ add_action( 'archive_blog', array( $this, 'EventArchiveBlog' ) );
54
+ add_action( 'unarchive_blog', array( $this, 'EventUnarchiveBlog' ) );
55
+ add_action( 'activate_blog', array( $this, 'EventActivateBlog' ) );
56
+ add_action( 'deactivate_blog', array( $this, 'EventDeactivateBlog' ) );
57
+ add_action( 'delete_blog', array( $this, 'EventDeleteBlog' ) );
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
+ }
62
+
63
+ /**
64
+ * Triggered when a user accesses the admin area.
65
+ */
66
+ public function EventAdminInit() {
67
+ $this->old_allowedthemes = array_keys( (array) get_site_option( 'allowedthemes' ) );
68
+ }
69
+
70
+ /**
71
+ * Activated/Deactivated theme on network.
72
+ */
73
+ public function EventAdminShutdown() {
74
+ if ( is_null( $this->old_allowedthemes ) ) {
75
+ return;
76
+ }
77
+ $new_allowedthemes = array_keys( (array) get_site_option( 'allowedthemes' ) );
78
+
79
+ // Check for enabled themes.
80
+ foreach ( $new_allowedthemes as $theme ) {
81
+ if ( ! in_array( $theme, (array) $this->old_allowedthemes ) ) {
82
+ $theme = wp_get_theme( $theme );
83
+ $this->plugin->alerts->Trigger(
84
+ 5008, array(
85
+ 'Theme' => (object) array(
86
+ 'Name' => $theme->Name,
87
+ 'ThemeURI' => $theme->ThemeURI,
88
+ 'Description' => $theme->Description,
89
+ 'Author' => $theme->Author,
90
+ 'Version' => $theme->Version,
91
+ 'get_template_directory' => $theme->get_template_directory(),
92
+ ),
93
+ )
94
+ );
95
+ }
96
+ }
97
+
98
+ // Check for disabled themes.
99
+ foreach ( (array) $this->old_allowedthemes as $theme ) {
100
+ if ( ! in_array( $theme, $new_allowedthemes ) ) {
101
+ $theme = wp_get_theme( $theme );
102
+ $this->plugin->alerts->Trigger(
103
+ 5009, array(
104
+ 'Theme' => (object) array(
105
+ 'Name' => $theme->Name,
106
+ 'ThemeURI' => $theme->ThemeURI,
107
+ 'Description' => $theme->Description,
108
+ 'Author' => $theme->Author,
109
+ 'Version' => $theme->Version,
110
+ 'get_template_directory' => $theme->get_template_directory(),
111
+ ),
112
+ )
113
+ );
114
+ }
115
+ }
116
+ }
117
+
118
+ /**
119
+ * New site added on the network.
120
+ *
121
+ * @param int $blog_id - Blog ID.
122
+ */
123
+ public function EventNewBlog( $blog_id ) {
124
+ $this->plugin->alerts->Trigger(
125
+ 7000, array(
126
+ 'BlogID' => $blog_id,
127
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
128
+ )
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Existing site archived.
134
+ *
135
+ * @param int $blog_id - Blog ID.
136
+ */
137
+ public function EventArchiveBlog( $blog_id ) {
138
+ $this->plugin->alerts->Trigger(
139
+ 7001, array(
140
+ 'BlogID' => $blog_id,
141
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
142
+ )
143
+ );
144
+ }
145
+
146
+ /**
147
+ * Archived site has been unarchived.
148
+ *
149
+ * @param int $blog_id - Blog ID.
150
+ */
151
+ public function EventUnarchiveBlog( $blog_id ) {
152
+ $this->plugin->alerts->Trigger(
153
+ 7002, array(
154
+ 'BlogID' => $blog_id,
155
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
156
+ )
157
+ );
158
+ }
159
+
160
+ /**
161
+ * Deactivated site has been activated.
162
+ *
163
+ * @param int $blog_id - Blog ID.
164
+ */
165
+ public function EventActivateBlog( $blog_id ) {
166
+ $this->plugin->alerts->Trigger(
167
+ 7003, array(
168
+ 'BlogID' => $blog_id,
169
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
170
+ )
171
+ );
172
+ }
173
+
174
+ /**
175
+ * Site has been deactivated.
176
+ *
177
+ * @param int $blog_id - Blog ID.
178
+ */
179
+ public function EventDeactivateBlog( $blog_id ) {
180
+ $this->plugin->alerts->Trigger(
181
+ 7004, array(
182
+ 'BlogID' => $blog_id,
183
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
184
+ )
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Existing site deleted from network.
190
+ *
191
+ * @param int $blog_id - Blog ID.
192
+ */
193
+ public function EventDeleteBlog( $blog_id ) {
194
+ $this->plugin->alerts->Trigger(
195
+ 7005, array(
196
+ 'BlogID' => $blog_id,
197
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
198
+ )
199
+ );
200
+ }
201
+
202
+ /**
203
+ * Existing user added to a site.
204
+ *
205
+ * @param int $user_id - User ID.
206
+ * @param string $role - User role.
207
+ * @param int $blog_id - Blog ID.
208
+ */
209
+ public function EventUserAddedToBlog( $user_id, $role, $blog_id ) {
210
+ $this->plugin->alerts->TriggerIf(
211
+ 4010, array(
212
+ 'TargetUserID' => $user_id,
213
+ 'TargetUsername' => get_userdata( $user_id )->user_login,
214
+ 'TargetUserRole' => $role,
215
+ 'BlogID' => $blog_id,
216
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
217
+ ), array( $this, 'MustNotContainCreateUser' )
218
+ );
219
+ }
220
+
221
+ /**
222
+ * User removed from site.
223
+ *
224
+ * @param int $user_id - User ID.
225
+ * @param int $blog_id - Blog ID.
226
+ */
227
+ public function EventUserRemovedFromBlog( $user_id, $blog_id ) {
228
+ $user = get_userdata( $user_id );
229
+ // $blog_id = (isset( $_REQUEST['id'] ) ? $_REQUEST['id'] : 0);
230
+ $this->plugin->alerts->TriggerIf(
231
+ 4011, array(
232
+ 'TargetUserID' => $user_id,
233
+ 'TargetUsername' => $user->user_login,
234
+ 'TargetUserRole' => is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles,
235
+ 'BlogID' => $blog_id,
236
+ 'SiteName' => get_blog_option( $blog_id, 'blogname' ),
237
+ ), array( $this, 'MustNotContainCreateUser' )
238
+ );
239
+ }
240
+
241
+ /**
242
+ * New network user created.
243
+ *
244
+ * @param WSAL_AlertManager $mgr - Instance of Alert Manager.
245
+ */
246
+ public function MustNotContainCreateUser( WSAL_AlertManager $mgr ) {
247
+ return ! $mgr->WillTrigger( 4012 );
248
+ }
249
  }
classes/Sensors/PhpErrors.php CHANGED
@@ -1,139 +1,177 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
5
- * Php Errors sensor.
 
 
 
 
 
 
 
6
  *
7
  * 0001 PHP error
8
  * 0002 PHP warning
9
  * 0003 PHP notice
10
  * 0004 PHP exception
11
  * 0005 PHP shutdown error
 
 
 
12
  */
13
- class WSAL_Sensors_PhpErrors extends WSAL_AbstractSensor
14
- {
15
- protected $_avoid_error_recursion = false;
16
- protected $_maybe_last_error = null;
17
-
18
- protected $_error_types = array(
19
- 0001 => array(1,4,16,64,256,4096), // errors
20
- 0002 => array(2,32,128,512), // warnings
21
- 0003 => array(8,1024,2048,8192,16384), // notices
22
- 0004 => array(), // exceptions
23
- 0005 => array(), // shutdown
24
- );
25
-
26
- /**
27
- * Listening to Php events.
28
- */
29
- public function HookEvents()
30
- {
31
- if ($this->plugin->settings->IsPhpErrorLoggingEnabled()) {
32
- set_error_handler(array($this, 'EventError'), E_ALL);
33
- set_exception_handler(array($this, 'EventException'));
34
- register_shutdown_function(array($this, 'EventShutdown'));
35
- }
36
- }
37
-
38
- /**
39
- * Get the hash of the error.
40
- */
41
- protected function GetErrorHash($code, $mesg, $file, $line)
42
- {
43
- return md5(implode(':', func_get_args()));
44
- }
45
-
46
- /**
47
- * PHP error, warning or notice.
48
- */
49
- public function EventError($errno, $errstr, $errfile = 'unknown', $errline = 0, $errcontext = array())
50
- {
51
- if ($this->_avoid_error_recursion) {
52
- return;
53
- }
54
-
55
- $errbacktrace = 'No Backtrace';
56
- if ($this->plugin->settings->IsBacktraceLoggingEnabled()) {
57
- ob_start();
58
- debug_print_backtrace();
59
- $errbacktrace = ob_get_clean();
60
- }
61
-
62
- $data = array(
63
- 'Code' => $errno,
64
- 'Message' => $errstr,
65
- 'File' => $errfile,
66
- 'Line' => $errline,
67
- 'Context' => $errcontext,
68
- 'Trace' => $errbacktrace,
69
- );
70
-
71
- $type = 0002; // default (middle ground)
72
- foreach ($this->_error_types as $temp => $codes) {
73
- if (in_array($errno, $codes)) {
74
- $type = $temp;
75
- }
76
- }
77
-
78
- $this->_maybe_last_error = $this->GetErrorHash($errno, $errstr, $errfile, $errline);
79
-
80
- $this->_avoid_error_recursion = true;
81
- $this->plugin->alerts->Trigger($type, $data);
82
- $this->_avoid_error_recursion = false;
83
- }
84
-
85
- /**
86
- * PHP exception.
87
- */
88
- public function EventException(Exception $ex)
89
- {
90
- if ($this->_avoid_error_recursion) {
91
- return;
92
- }
93
-
94
- $errbacktrace = 'No Backtrace';
95
- if ($this->plugin->settings->IsBacktraceLoggingEnabled()) {
96
- $errbacktrace = $ex->getTraceAsString();
97
- }
98
-
99
- $data = array(
100
- 'Class' => get_class($ex),
101
- 'Code' => $ex->getCode(),
102
- 'Message' => $ex->getMessage(),
103
- 'File' => $ex->getFile(),
104
- 'Line' => $ex->getLine(),
105
- 'Trace' => $errbacktrace,
106
- );
107
-
108
- if (method_exists($ex, 'getContext')) {
109
- $data['Context'] = $ex->getContext();
110
- }
111
-
112
- $this->_avoid_error_recursion = true;
113
- $this->plugin->alerts->Trigger(0004, $data);
114
- $this->_avoid_error_recursion = false;
115
- }
116
-
117
- /**
118
- * PHP shutdown error.
119
- */
120
- public function EventShutdown()
121
- {
122
- if ($this->_avoid_error_recursion) {
123
- return;
124
- }
125
-
126
- if (!!($e = error_get_last()) && ($this->_maybe_last_error != $this->GetErrorHash($e['type'], $e['message'], $e['file'], $e['line']))) {
127
- $data = array(
128
- 'Code' => $e['type'],
129
- 'Message' => $e['message'],
130
- 'File' => $e['file'],
131
- 'Line' => $e['line'],
132
- );
133
-
134
- $this->_avoid_error_recursion = true;
135
- $this->plugin->alerts->Trigger(0005, $data);
136
- $this->_avoid_error_recursion = false;
137
- }
138
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  }
1
  <?php
2
  /**
3
+ * Sensor: PHP Errors
4
+ *
5
+ * PHP Errors sensor file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
+ * PHP Errors sensor.
18
  *
19
  * 0001 PHP error
20
  * 0002 PHP warning
21
  * 0003 PHP notice
22
  * 0004 PHP exception
23
  * 0005 PHP shutdown error
24
+ *
25
+ * @package Wsal
26
+ * @subpackage Sensors
27
  */
28
+ class WSAL_Sensors_PhpErrors extends WSAL_AbstractSensor {
29
+
30
+ /**
31
+ * Avoid Recursive Errors
32
+ *
33
+ * @var boolean
34
+ */
35
+ protected $_avoid_error_recursion = false;
36
+
37
+ /**
38
+ * Last Error
39
+ *
40
+ * @var string
41
+ */
42
+ protected $_maybe_last_error = null;
43
+
44
+ /**
45
+ * Error Types
46
+ *
47
+ * @var array
48
+ */
49
+ protected $_error_types = array(
50
+ 0001 => array( 1, 4, 16, 64, 256, 4096 ), // Errors.
51
+ 0002 => array( 2, 32, 128, 512 ), // Warnings.
52
+ 0003 => array( 8, 1024, 2048, 8192, 16384 ), // Notices.
53
+ 0004 => array(), // Exceptions.
54
+ 0005 => array(), // Shutdown.
55
+ );
56
+
57
+ /**
58
+ * Listening to Php events.
59
+ */
60
+ public function HookEvents() {
61
+ if ( $this->plugin->settings->IsPhpErrorLoggingEnabled() ) {
62
+ set_error_handler( array( $this, 'EventError' ), E_ALL );
63
+ set_exception_handler( array( $this, 'EventException' ) );
64
+ register_shutdown_function( array( $this, 'EventShutdown' ) );
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Get the hash of the error.
70
+ *
71
+ * @param integer $code - Error code.
72
+ * @param string $mesg - Error message.
73
+ * @param string $file - File name.
74
+ * @param integer $line - Line number.
75
+ */
76
+ protected function GetErrorHash( $code, $mesg, $file, $line ) {
77
+ return md5( implode( ':', func_get_args() ) );
78
+ }
79
+
80
+ /**
81
+ * PHP error, warning or notice.
82
+ *
83
+ * @param integer $errno - Error code.
84
+ * @param string $errstr - Error message.
85
+ * @param string $errfile - File name.
86
+ * @param integer $errline - Line number.
87
+ * @param array $errcontext - Error context.
88
+ */
89
+ public function EventError( $errno, $errstr, $errfile = 'unknown', $errline = 0, $errcontext = array() ) {
90
+ if ( $this->_avoid_error_recursion ) {
91
+ return;
92
+ }
93
+
94
+ $errbacktrace = 'No Backtrace';
95
+ if ( $this->plugin->settings->IsBacktraceLoggingEnabled() ) {
96
+ ob_start();
97
+ debug_print_backtrace();
98
+ $errbacktrace = ob_get_clean();
99
+ }
100
+
101
+ $data = array(
102
+ 'Code' => $errno,
103
+ 'Message' => $errstr,
104
+ 'File' => $errfile,
105
+ 'Line' => $errline,
106
+ 'Context' => $errcontext,
107
+ 'Trace' => $errbacktrace,
108
+ );
109
+
110
+ $type = 0002; // Default — middle ground.
111
+ foreach ( $this->_error_types as $temp => $codes ) {
112
+ if ( in_array( $errno, $codes ) ) {
113
+ $type = $temp;
114
+ }
115
+ }
116
+
117
+ $this->_maybe_last_error = $this->GetErrorHash( $errno, $errstr, $errfile, $errline );
118
+ $this->_avoid_error_recursion = true;
119
+ $this->plugin->alerts->Trigger( $type, $data );
120
+ $this->_avoid_error_recursion = false;
121
+ }
122
+
123
+ /**
124
+ * PHP exception.
125
+ *
126
+ * @param Exception $ex - Instance of Exception.
127
+ */
128
+ public function EventException( Exception $ex ) {
129
+ if ( $this->_avoid_error_recursion ) {
130
+ return;
131
+ }
132
+
133
+ $errbacktrace = 'No Backtrace';
134
+ if ( $this->plugin->settings->IsBacktraceLoggingEnabled() ) {
135
+ $errbacktrace = $ex->getTraceAsString();
136
+ }
137
+
138
+ $data = array(
139
+ 'Class' => get_class( $ex ),
140
+ 'Code' => $ex->getCode(),
141
+ 'Message' => $ex->getMessage(),
142
+ 'File' => $ex->getFile(),
143
+ 'Line' => $ex->getLine(),
144
+ 'Trace' => $errbacktrace,
145
+ );
146
+
147
+ if ( method_exists( $ex, 'getContext' ) ) {
148
+ $data['Context'] = $ex->getContext();
149
+ }
150
+
151
+ $this->_avoid_error_recursion = true;
152
+ $this->plugin->alerts->Trigger( 0004, $data );
153
+ $this->_avoid_error_recursion = false;
154
+ }
155
+
156
+ /**
157
+ * PHP shutdown error.
158
+ */
159
+ public function EventShutdown() {
160
+ if ( $this->_avoid_error_recursion ) {
161
+ return;
162
+ }
163
+
164
+ if ( ! ! ($e = error_get_last()) && ($this->_maybe_last_error != $this->GetErrorHash( $e['type'], $e['message'], $e['file'], $e['line'] )) ) {
165
+ $data = array(
166
+ 'Code' => $e['type'],
167
+ 'Message' => $e['message'],
168
+ 'File' => $e['file'],
169
+ 'Line' => $e['line'],
170
+ );
171
+
172
+ $this->_avoid_error_recursion = true;
173
+ $this->plugin->alerts->Trigger( 0005, $data );
174
+ $this->_avoid_error_recursion = false;
175
+ }
176
+ }
177
  }
classes/Sensors/PluginsThemes.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Plugins & Themes sensor.
6
  *
7
  * 5000 User installed a plugin
@@ -22,10 +34,24 @@
22
  * 2106 A plugin modified a post
23
  * 2107 A plugin modified a page
24
  * 2108 A plugin modified a custom post
 
 
 
25
  */
26
  class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
27
 
 
 
 
 
 
28
  protected $old_themes = array();
 
 
 
 
 
 
29
  protected $old_plugins = array();
30
 
31
  /**
@@ -57,165 +83,294 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
57
  /**
58
  * Install, uninstall, activate, deactivate, upgrade and update.
59
  */
60
- public function EventAdminShutdown()
61
- {
62
- $action = (isset($_REQUEST['action']) && $_REQUEST['action'] != "-1") ? $_REQUEST['action'] : '';
63
- $action = (isset($_REQUEST['action2']) && $_REQUEST['action2'] != "-1") ? $_REQUEST['action2'] : $action;
64
- $actype = basename($_SERVER['SCRIPT_NAME'], '.php');
65
- $is_themes = $actype == 'themes';
66
- $is_plugins = $actype == 'plugins';
67
-
68
- // install plugin
69
- if (in_array($action, array('install-plugin', 'upload-plugin')) && current_user_can("install_plugins")) {
70
- $plugin = array_values(array_diff(array_keys(get_plugins()), array_keys($this->old_plugins)));
71
- if (count($plugin) != 1) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  return $this->LogError(
73
- 'Expected exactly one new plugin but found ' . count($plugin),
74
- array('NewPlugin' => $plugin, 'OldPlugins' => $this->old_plugins, 'NewPlugins' => get_plugins())
 
 
 
 
75
  );
76
  }
77
- $pluginPath = $plugin[0];
78
  $plugin = get_plugins();
79
- $plugin = $plugin[$pluginPath];
80
- $pluginPath = plugin_dir_path(WP_PLUGIN_DIR . '/' . $pluginPath[0]);
81
- $this->plugin->alerts->Trigger(5000, array(
82
- 'Plugin' => (object)array(
83
- 'Name' => $plugin['Name'],
84
- 'PluginURI' => $plugin['PluginURI'],
85
- 'Version' => $plugin['Version'],
86
- 'Author' => $plugin['Author'],
87
- 'Network' => $plugin['Network'] ? 'True' : 'False',
88
- 'plugin_dir_path' => $pluginPath,
89
- ),
90
- ));
 
 
91
  }
92
 
93
- // activate plugin
94
- if ($is_plugins && in_array($action, array('activate', 'activate-selected')) && current_user_can("activate_plugins")) {
95
- if (isset($_REQUEST['plugin'])) {
96
- if (!isset($_REQUEST['checked'])) {
97
- $_REQUEST['checked'] = array();
 
98
  }
99
- $_REQUEST['checked'][] = $_REQUEST['plugin'];
100
  }
101
- foreach ($_REQUEST['checked'] as $pluginFile) {
102
- $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
103
- $pluginData = get_plugin_data($pluginFile, false, true);
104
- $this->plugin->alerts->Trigger(5001, array(
105
- 'PluginFile' => $pluginFile,
106
- 'PluginData' => (object)array(
107
- 'Name' => $pluginData['Name'],
108
- 'PluginURI' => $pluginData['PluginURI'],
109
- 'Version' => $pluginData['Version'],
110
- 'Author' => $pluginData['Author'],
111
- 'Network' => $pluginData['Network'] ? 'True' : 'False',
112
- ),
113
- ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  }
115
  }
116
 
117
- // deactivate plugin
118
- if ($is_plugins && in_array($action, array('deactivate', 'deactivate-selected')) && current_user_can("activate_plugins")) {
119
- if (isset($_REQUEST['plugin'])) {
120
- if (!isset($_REQUEST['checked'])) {
121
- $_REQUEST['checked'] = array();
 
122
  }
123
- $_REQUEST['checked'][] = $_REQUEST['plugin'];
124
  }
125
- foreach ($_REQUEST['checked'] as $pluginFile) {
126
- $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
127
- $pluginData = get_plugin_data($pluginFile, false, true);
128
- $this->plugin->alerts->Trigger(5002, array(
129
- 'PluginFile' => $pluginFile,
130
- 'PluginData' => (object)array(
131
- 'Name' => $pluginData['Name'],
132
- 'PluginURI' => $pluginData['PluginURI'],
133
- 'Version' => $pluginData['Version'],
134
- 'Author' => $pluginData['Author'],
135
- 'Network' => $pluginData['Network'] ? 'True' : 'False',
136
- ),
137
- ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
139
  }
140
 
141
- // uninstall plugin
142
- if ($is_plugins && in_array($action, array('delete-selected')) && current_user_can("delete_plugins")) {
143
- if (!isset($_REQUEST['verify-delete'])) {
144
- // first step, before user approves deletion
145
- // TODO store plugin data in session here
146
  } else {
147
  // second step, after deletion approval
148
- // TODO use plugin data from session
149
- foreach ($_REQUEST['checked'] as $pluginFile) {
150
- $pluginName = basename($pluginFile, '.php');
151
- $pluginName = str_replace(array('_', '-', ' '), ' ', $pluginName);
152
- $pluginName = ucwords($pluginName);
153
- $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
154
- $this->plugin->alerts->Trigger(5003, array(
155
- 'PluginFile' => $pluginFile,
156
- 'PluginData' => (object)array(
157
- 'Name' => $pluginName,
158
- ),
159
- ));
 
 
160
  }
161
  }
162
  }
163
 
164
- // uninstall plugin for Wordpress version 4.6
165
- if (in_array($action, array('delete-plugin')) && current_user_can("delete_plugins")) {
166
- if (isset($_REQUEST['plugin'])) {
167
- $pluginFile = WP_PLUGIN_DIR . '/' . $_REQUEST['plugin'];
168
- $pluginName = basename($pluginFile, '.php');
169
- $pluginName = str_replace(array('_', '-', ' '), ' ', $pluginName);
170
- $pluginName = ucwords($pluginName);
171
- $this->plugin->alerts->Trigger(5003, array(
172
- 'PluginFile' => $pluginFile,
173
- 'PluginData' => (object)array(
174
- 'Name' => $pluginName,
175
- ),
176
- ));
 
 
177
  }
178
  }
179
 
180
- // upgrade plugin
181
- if (in_array($action, array('upgrade-plugin', 'update-plugin', 'update-selected')) && current_user_can("update_plugins")) {
182
  $plugins = array();
183
- if (isset($_REQUEST['plugins'])) {
184
- $plugins = explode(",", $_REQUEST['plugins']);
185
- } else if (isset($_REQUEST['plugin'])) {
186
- $plugins[] = $_REQUEST['plugin'];
 
 
187
  }
188
- if (isset($plugins)) {
189
- foreach ($plugins as $pluginFile) {
190
- $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
191
- $pluginData = get_plugin_data($pluginFile, false, true);
192
- $this->plugin->alerts->Trigger(5004, array(
193
- 'PluginFile' => $pluginFile,
194
- 'PluginData' => (object)array(
195
- 'Name' => $pluginData['Name'],
196
- 'PluginURI' => $pluginData['PluginURI'],
197
- 'Version' => $pluginData['Version'],
198
- 'Author' => $pluginData['Author'],
199
- 'Network' => $pluginData['Network'] ? 'True' : 'False',
200
- ),
201
- ));
 
 
 
 
 
 
 
 
 
202
  }
203
  }
204
  }
205
 
206
- // update theme
207
- if (in_array($action, array('upgrade-theme', 'update-theme', 'update-selected-themes')) && current_user_can("install_themes")) {
 
208
  $themes = array();
209
- if (isset($_REQUEST['slug']) || isset($_REQUEST['theme'])) {
210
- $themes[] = isset($_REQUEST['slug']) ? $_REQUEST['slug'] : $_REQUEST['theme'];
211
- } elseif (isset($_REQUEST['themes'])) {
212
- $themes = explode(",", $_REQUEST['themes']);
 
 
 
 
 
 
 
 
 
213
  }
214
- if (isset($themes)) {
215
- foreach ($themes as $theme_name) {
216
- $theme = wp_get_theme($theme_name);
217
- $this->plugin->alerts->Trigger(5031, array(
218
- 'Theme' => (object)array(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  'Name' => $theme->Name,
220
  'ThemeURI' => $theme->ThemeURI,
221
  'Description' => $theme->Description,
@@ -223,105 +378,132 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
223
  'Version' => $theme->Version,
224
  'get_template_directory' => $theme->get_template_directory(),
225
  ),
226
- ));
227
- }
228
- }
229
- }
230
-
231
- // install theme
232
- if (in_array($action, array('install-theme', 'upload-theme')) && current_user_can("install_themes")) {
233
- $themes = array_diff(wp_get_themes(), $this->old_themes);
234
- foreach ($themes as $theme) {
235
- $this->plugin->alerts->Trigger(5005, array(
236
- 'Theme' => (object)array(
237
- 'Name' => $theme->Name,
238
- 'ThemeURI' => $theme->ThemeURI,
239
- 'Description' => $theme->Description,
240
- 'Author' => $theme->Author,
241
- 'Version' => $theme->Version,
242
- 'get_template_directory' => $theme->get_template_directory(),
243
- ),
244
- ));
245
  }
246
  }
247
 
248
- // uninstall theme
249
- if (in_array($action, array('delete-theme')) && current_user_can("install_themes")) {
250
- foreach ($this->GetRemovedThemes() as $theme) {
251
- $this->plugin->alerts->Trigger(5007, array(
252
- 'Theme' => (object)array(
253
- 'Name' => $theme->Name,
254
- 'ThemeURI' => $theme->ThemeURI,
255
- 'Description' => $theme->Description,
256
- 'Author' => $theme->Author,
257
- 'Version' => $theme->Version,
258
- 'get_template_directory' => $theme->get_template_directory(),
259
- ),
260
- ));
 
 
261
  }
262
  }
263
  }
264
 
265
  /**
266
  * Activated a theme.
267
- * @param string $themeName name
 
268
  */
269
- public function EventThemeActivated($themeName)
270
- {
271
  $theme = null;
272
- foreach (wp_get_themes() as $item) {
273
- if ($item->Name == $themeName) {
274
  $theme = $item;
275
  break;
276
  }
277
  }
278
- if ($theme == null) {
279
  return $this->LogError(
280
  'Could not locate theme named "' . $theme . '".',
281
- array('ThemeName' => $themeName, 'Themes' => wp_get_themes())
 
 
 
282
  );
283
  }
284
- $this->plugin->alerts->Trigger(5006, array(
285
- 'Theme' => (object)array(
286
- 'Name' => $theme->Name,
287
- 'ThemeURI' => $theme->ThemeURI,
288
- 'Description' => $theme->Description,
289
- 'Author' => $theme->Author,
290
- 'Version' => $theme->Version,
291
- 'get_template_directory' => $theme->get_template_directory(),
292
- ),
293
- ));
 
 
294
  }
295
 
296
  /**
297
  * Plugin creates/modifies posts.
298
  *
299
- * @param int $post_id - Post ID.
300
  * @param object $post - Post object.
301
  */
302
  public function EventPluginPostCreate( $post_id, $post ) {
303
- $WPActions = array( 'editpost', 'heartbeat', 'inline-save', 'trash', 'untrash' );
304
- if ( isset( $_REQUEST['action'] ) && ! in_array( $_REQUEST['action'], $WPActions ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  if ( ! in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item', 'customize_changeset', 'custom_css' ) )
306
  || ! empty( $post->post_title ) ) {
307
  // If the plugin modify the post.
308
- if ( false !== strpos( $_REQUEST['action'], 'edit' ) ) {
309
  $event = $this->GetEventTypeForPostType( $post, 2106, 2107, 2108 );
310
- $editorLink = $this->GetEditorLink( $post );
311
- $this->plugin->alerts->Trigger( $event, array(
312
- 'PostID' => $post->ID,
313
- 'PostType' => $post->post_type,
314
- 'PostTitle' => $post->post_title,
315
- $editorLink['name'] => $editorLink['value'],
316
- ) );
 
 
317
  } else {
318
  $event = $this->GetEventTypeForPostType( $post, 5019, 5020, 5021 );
319
- $this->plugin->alerts->Trigger( $event, array(
320
- 'PostID' => $post->ID,
321
- 'PostType' => $post->post_type,
322
- 'PostTitle' => $post->post_title,
323
- 'Username' => 'Plugins',
324
- ) );
 
 
325
  }
326
  }
327
  }
@@ -333,51 +515,78 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
333
  * @param integer $post_id - Post ID.
334
  */
335
  public function EventPluginPostDelete( $post_id ) {
336
- if ( empty( $_REQUEST['action'] ) && isset( $_REQUEST['page'] ) ) {
 
 
 
 
337
  $post = get_post( $post_id );
338
  if ( ! in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item', 'customize_changeset', 'custom_css' ) )
339
  || ! empty( $post->post_title ) ) {
340
  $event = $this->GetEventTypeForPostType( $post, 5025, 5026, 5027 );
341
- $this->plugin->alerts->Trigger($event, array(
342
- 'PostID' => $post->ID,
343
- 'PostType' => $post->post_type,
344
- 'PostTitle' => $post->post_title,
345
- 'Username' => 'Plugins',
346
- ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  }
348
  }
349
  }
350
 
351
  /**
352
  * Get removed themes.
 
353
  * @return array of WP_Theme objects
354
  */
355
- protected function GetRemovedThemes()
356
- {
357
  $result = $this->old_themes;
358
- foreach ($result as $i => $theme) {
359
- if (file_exists($theme->get_template_directory())) {
360
- unset($result[$i]);
361
  }
362
  }
363
- return array_values($result);
364
  }
365
 
366
  /**
367
  * Get event code by post type.
 
 
 
 
 
368
  */
369
- protected function GetEventTypeForPostType( $post, $typePost, $typePage, $typeCustom ) {
370
  if ( empty( $post ) || ! isset( $post->post_type ) ) {
371
  return false;
372
  }
373
 
374
  switch ( $post->post_type ) {
375
  case 'page':
376
- return $typePage;
377
  case 'post':
378
- return $typePost;
379
  default:
380
- return $typeCustom;
381
  }
382
  }
383
 
@@ -385,16 +594,16 @@ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
385
  * Get editor link.
386
  *
387
  * @param object $post - The post object.
388
- * @return array $aLink name and value link.
389
  */
390
  private function GetEditorLink( $post ) {
391
  $name = 'EditorLink';
392
- $name .= ( $post->post_type == 'page' ) ? 'Page' : 'Post' ;
393
  $value = get_edit_post_link( $post->ID );
394
- $aLink = array(
395
- 'name' => $name,
396
  'value' => $value,
397
  );
398
- return $aLink;
399
  }
400
  }
1
  <?php
2
  /**
3
+ * Sensor: Plugins & Themes
4
+ *
5
+ * Plugins & Themes sensor file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Plugins & Themes sensor.
18
  *
19
  * 5000 User installed a plugin
34
  * 2106 A plugin modified a post
35
  * 2107 A plugin modified a page
36
  * 2108 A plugin modified a custom post
37
+ *
38
+ * @package Wsal
39
+ * @subpackage Sensors
40
  */
41
  class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
42
 
43
+ /**
44
+ * List of Themes.
45
+ *
46
+ * @var array
47
+ */
48
  protected $old_themes = array();
49
+
50
+ /**
51
+ * List of Plugins.
52
+ *
53
+ * @var array
54
+ */
55
  protected $old_plugins = array();
56
 
57
  /**
83
  /**
84
  * Install, uninstall, activate, deactivate, upgrade and update.
85
  */
86
+ public function EventAdminShutdown() {
87
+ // Filter global arrays for security.
88
+ $post_array = filter_input_array( INPUT_POST );
89
+ $get_array = filter_input_array( INPUT_GET );
90
+ $server_array = filter_input_array( INPUT_SERVER );
91
+
92
+ $action = '';
93
+ if ( isset( $get_array['action'] ) && '-1' != $get_array['action'] ) {
94
+ $action = $get_array['action'];
95
+ } elseif ( isset( $post_array['action'] ) && '-1' != $post_array['action'] ) {
96
+ $action = $post_array['action'];
97
+ }
98
+
99
+ if ( isset( $get_array['action2'] ) && '-1' != $get_array['action2'] ) {
100
+ $action = $get_array['action2'];
101
+ } elseif ( isset( $post_array['action2'] ) && '-1' != $post_array['action2'] ) {
102
+ $action = $post_array['action2'];
103
+ }
104
+
105
+ $actype = basename( $server_array['SCRIPT_NAME'], '.php' );
106
+ $is_themes = 'themes' == $actype;
107
+ $is_plugins = 'plugins' == $actype;
108
+
109
+ // Install plugin.
110
+ if ( in_array( $action, array( 'install-plugin', 'upload-plugin' ) ) && current_user_can( 'install_plugins' ) ) {
111
+ $plugin = array_values( array_diff( array_keys( get_plugins() ), array_keys( $this->old_plugins ) ) );
112
+ if ( count( $plugin ) != 1 ) {
113
  return $this->LogError(
114
+ 'Expected exactly one new plugin but found ' . count( $plugin ),
115
+ array(
116
+ 'NewPlugin' => $plugin,
117
+ 'OldPlugins' => $this->old_plugins,
118
+ 'NewPlugins' => get_plugins(),
119
+ )
120
  );
121
  }
122
+ $plugin_path = $plugin[0];
123
  $plugin = get_plugins();
124
+ $plugin = $plugin[ $plugin_path ];
125
+ $plugin_path = plugin_dir_path( WP_PLUGIN_DIR . '/' . $plugin_path[0] );
126
+ $this->plugin->alerts->Trigger(
127
+ 5000, array(
128
+ 'Plugin' => (object) array(
129
+ 'Name' => $plugin['Name'],
130
+ 'PluginURI' => $plugin['PluginURI'],
131
+ 'Version' => $plugin['Version'],
132
+ 'Author' => $plugin['Author'],
133
+ 'Network' => $plugin['Network'] ? 'True' : 'False',
134
+ 'plugin_dir_path' => $plugin_path,
135
+ ),
136
+ )
137
+ );
138
  }
139
 
140
+ // Activate plugin.
141
+ if ( $is_plugins && in_array( $action, array( 'activate', 'activate-selected' ) ) && current_user_can( 'activate_plugins' ) ) {
142
+ // Check $_GET array case.
143
+ if ( isset( $get_array['plugin'] ) ) {
144
+ if ( ! isset( $get_array['checked'] ) ) {
145
+ $get_array['checked'] = array();
146
  }
147
+ $get_array['checked'][] = $get_array['plugin'];
148
  }
149
+
150
+ // Check $_POST array case.
151
+ if ( isset( $post_array['plugin'] ) ) {
152
+ if ( ! isset( $post_array['checked'] ) ) {
153
+ $post_array['checked'] = array();
154
+ }
155
+ $post_array['checked'][] = $post_array['plugin'];
156
+ }
157
+
158
+ if ( isset( $get_array['checked'] ) && ! empty( $get_array['checked'] ) ) {
159
+ foreach ( $get_array['checked'] as $plugin_file ) {
160
+ $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
161
+ $plugin_data = get_plugin_data( $plugin_file, false, true );
162
+ $this->plugin->alerts->Trigger(
163
+ 5001, array(
164
+ 'PluginFile' => $plugin_file,
165
+ 'PluginData' => (object) array(
166
+ 'Name' => $plugin_data['Name'],
167
+ 'PluginURI' => $plugin_data['PluginURI'],
168
+ 'Version' => $plugin_data['Version'],
169
+ 'Author' => $plugin_data['Author'],
170
+ 'Network' => $plugin_data['Network'] ? 'True' : 'False',
171
+ ),
172
+ )
173
+ );
174
+ }
175
+ } elseif ( isset( $post_array['checked'] ) && ! empty( $post_array['checked'] ) ) {
176
+ foreach ( $post_array['checked'] as $plugin_file ) {
177
+ $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
178
+ $plugin_data = get_plugin_data( $plugin_file, false, true );
179
+ $this->plugin->alerts->Trigger(
180
+ 5001, array(
181
+ 'PluginFile' => $plugin_file,
182
+ 'PluginData' => (object) array(
183
+ 'Name' => $plugin_data['Name'],
184
+ 'PluginURI' => $plugin_data['PluginURI'],
185
+ 'Version' => $plugin_data['Version'],
186
+ 'Author' => $plugin_data['Author'],
187
+ 'Network' => $plugin_data['Network'] ? 'True' : 'False',
188
+ ),
189
+ )
190
+ );
191
+ }
192
  }
193
  }
194
 
195
+ // Deactivate plugin.
196
+ if ( $is_plugins && in_array( $action, array( 'deactivate', 'deactivate-selected' ) ) && current_user_can( 'activate_plugins' ) ) {
197
+ // Check $_GET array case.
198
+ if ( isset( $get_array['plugin'] ) ) {
199
+ if ( ! isset( $get_array['checked'] ) ) {
200
+ $get_array['checked'] = array();
201
  }
202
+ $get_array['checked'][] = $get_array['plugin'];
203
  }
204
+
205
+ // Check $_POST array case.
206
+ if ( isset( $post_array['plugin'] ) ) {
207
+ if ( ! isset( $post_array['checked'] ) ) {
208
+ $post_array['checked'] = array();
209
+ }
210
+ $post_array['checked'][] = $post_array['plugin'];
211
+ }
212
+
213
+ if ( isset( $get_array['checked'] ) && ! empty( $get_array['checked'] ) ) {
214
+ foreach ( $get_array['checked'] as $plugin_file ) {
215
+ $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
216
+ $plugin_data = get_plugin_data( $plugin_file, false, true );
217
+ $this->plugin->alerts->Trigger(
218
+ 5002, array(
219
+ 'PluginFile' => $plugin_file,
220
+ 'PluginData' => (object) array(
221
+ 'Name' => $plugin_data['Name'],
222
+ 'PluginURI' => $plugin_data['PluginURI'],
223
+ 'Version' => $plugin_data['Version'],
224
+ 'Author' => $plugin_data['Author'],
225
+ 'Network' => $plugin_data['Network'] ? 'True' : 'False',
226
+ ),
227
+ )
228
+ );
229
+ }
230
+ } elseif ( isset( $post_array['checked'] ) && ! empty( $post_array['checked'] ) ) {
231
+ foreach ( $post_array['checked'] as $plugin_file ) {
232
+ $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
233
+ $plugin_data = get_plugin_data( $plugin_file, false, true );
234
+ $this->plugin->alerts->Trigger(
235
+ 5002, array(
236
+ 'PluginFile' => $plugin_file,
237
+ 'PluginData' => (object) array(
238
+ 'Name' => $plugin_data['Name'],
239
+ 'PluginURI' => $plugin_data['PluginURI'],
240
+ 'Version' => $plugin_data['Version'],
241
+ 'Author' => $plugin_data['Author'],
242
+ 'Network' => $plugin_data['Network'] ? 'True' : 'False',
243
+ ),
244
+ )
245
+ );
246
+ }
247
  }
248
  }
249
 
250
+ // Uninstall plugin.
251
+ if ( $is_plugins && in_array( $action, array( 'delete-selected' ) ) && current_user_can( 'delete_plugins' ) ) {
252
+ if ( ! isset( $post_array['verify-delete'] ) ) {
253
+ // First step, before user approves deletion
254
+ // TODO store plugin data in session here.
255
  } else {
256
  // second step, after deletion approval
257
+ // TODO use plugin data from session.
258
+ foreach ( $post_array['checked'] as $plugin_file ) {
259
+ $plugin_name = basename( $plugin_file, '.php' );
260
+ $plugin_name = str_replace( array( '_', '-', ' ' ), ' ', $plugin_name );
261
+ $plugin_name = ucwords( $plugin_name );
262
+ $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
263
+ $this->plugin->alerts->Trigger(
264
+ 5003, array(
265
+ 'PluginFile' => $plugin_file,
266
+ 'PluginData' => (object) array(
267
+ 'Name' => $plugin_name,
268
+ ),
269
+ )
270
+ );
271
  }
272
  }
273
  }
274
 
275
+ // Uninstall plugin for WordPress version 4.6.
276
+ if ( in_array( $action, array( 'delete-plugin' ) ) && current_user_can( 'delete_plugins' ) ) {
277
+ if ( isset( $post_array['plugin'] ) ) {
278
+ $plugin_file = WP_PLUGIN_DIR . '/' . $post_array['plugin'];
279
+ $plugin_name = basename( $plugin_file, '.php' );
280
+ $plugin_name = str_replace( array( '_', '-', ' ' ), ' ', $plugin_name );
281
+ $plugin_name = ucwords( $plugin_name );
282
+ $this->plugin->alerts->Trigger(
283
+ 5003, array(
284
+ 'PluginFile' => $plugin_file,
285
+ 'PluginData' => (object) array(
286
+ 'Name' => $plugin_name,
287
+ ),
288
+ )
289
+ );
290
  }
291
  }
292
 
293
+ // Upgrade plugin.
294
+ if ( in_array( $action, array( 'upgrade-plugin', 'update-plugin', 'update-selected' ) ) && current_user_can( 'update_plugins' ) ) {
295
  $plugins = array();
296
+
297
+ // Check $_GET array cases.
298
+ if ( isset( $get_array['plugins'] ) ) {
299
+ $plugins = explode( ',', $get_array['plugins'] );
300
+ } elseif ( isset( $get_array['plugin'] ) ) {
301
+ $plugins[] = $get_array['plugin'];
302
  }
303
+
304
+ // Check $_POST array cases.
305
+ if ( isset( $post_array['plugins'] ) ) {
306
+ $plugins = explode( ',', $post_array['plugins'] );
307
+ } elseif ( isset( $post_array['plugin'] ) ) {
308
+ $plugins[] = $post_array['plugin'];
309
+ }
310
+ if ( isset( $plugins ) ) {
311
+ foreach ( $plugins as $plugin_file ) {
312
+ $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_file;
313
+ $plugin_data = get_plugin_data( $plugin_file, false, true );
314
+ $this->plugin->alerts->Trigger(
315
+ 5004, array(
316
+ 'PluginFile' => $plugin_file,
317
+ 'PluginData' => (object) array(
318
+ 'Name' => $plugin_data['Name'],
319
+ 'PluginURI' => $plugin_data['PluginURI'],
320
+ 'Version' => $plugin_data['Version'],
321
+ 'Author' => $plugin_data['Author'],
322
+ 'Network' => $plugin_data['Network'] ? 'True' : 'False',
323
+ ),
324
+ )
325
+ );
326
  }
327
  }
328
  }
329
 
330
+ // Update theme.
331
+ if ( in_array( $action, array( 'upgrade-theme', 'update-theme', 'update-selected-themes' ) ) && current_user_can( 'install_themes' ) ) {
332
+ // Themes.
333
  $themes = array();
334
+
335
+ // Check $_GET array cases.
336
+ if ( isset( $get_array['slug'] ) || isset( $get_array['theme'] ) ) {
337
+ $themes[] = isset( $get_array['slug'] ) ? $get_array['slug'] : $get_array['theme'];
338
+ } elseif ( isset( $get_array['themes'] ) ) {
339
+ $themes = explode( ',', $get_array['themes'] );
340
+ }
341
+
342
+ // Check $_POST array cases.
343
+ if ( isset( $post_array['slug'] ) || isset( $post_array['theme'] ) ) {
344
+ $themes[] = isset( $post_array['slug'] ) ? $post_array['slug'] : $post_array['theme'];
345
+ } elseif ( isset( $post_array['themes'] ) ) {
346
+ $themes = explode( ',', $post_array['themes'] );
347
  }
348
+ if ( isset( $themes ) ) {
349
+ foreach ( $themes as $theme_name ) {
350
+ $theme = wp_get_theme( $theme_name );
351
+ $this->plugin->alerts->Trigger(
352
+ 5031, array(
353
+ 'Theme' => (object) array(
354
+ 'Name' => $theme->Name,
355
+ 'ThemeURI' => $theme->ThemeURI,
356
+ 'Description' => $theme->Description,
357
+ 'Author' => $theme->Author,
358
+ 'Version' => $theme->Version,
359
+ 'get_template_directory' => $theme->get_template_directory(),
360
+ ),
361
+ )
362
+ );
363
+ }
364
+ }
365
+ }
366
+
367
+ // Install theme.
368
+ if ( in_array( $action, array( 'install-theme', 'upload-theme' ) ) && current_user_can( 'install_themes' ) ) {
369
+ $themes = array_diff( wp_get_themes(), $this->old_themes );
370
+ foreach ( $themes as $theme ) {
371
+ $this->plugin->alerts->Trigger(
372
+ 5005, array(
373
+ 'Theme' => (object) array(
374
  'Name' => $theme->Name,
375
  'ThemeURI' => $theme->ThemeURI,
376
  'Description' => $theme->Description,
378
  'Version' => $theme->Version,
379
  'get_template_directory' => $theme->get_template_directory(),
380
  ),
381
+ )
382
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  }
384
  }
385
 
386
+ // Uninstall theme.
387
+ if ( in_array( $action, array( 'delete-theme' ) ) && current_user_can( 'install_themes' ) ) {
388
+ foreach ( $this->GetRemovedThemes() as $theme ) {
389
+ $this->plugin->alerts->Trigger(
390
+ 5007, array(
391
+ 'Theme' => (object) array(
392
+ 'Name' => $theme->Name,
393
+ 'ThemeURI' => $theme->ThemeURI,
394
+ 'Description' => $theme->Description,
395
+ 'Author' => $theme->Author,
396
+ 'Version' => $theme->Version,
397
+ 'get_template_directory' => $theme->get_template_directory(),
398
+ ),
399
+ )
400
+ );
401
  }
402
  }
403
  }
404
 
405
  /**
406
  * Activated a theme.
407
+ *
408
+ * @param string $theme_name - Theme name.
409
  */
410
+ public function EventThemeActivated( $theme_name ) {
 
411
  $theme = null;
412
+ foreach ( wp_get_themes() as $item ) {
413
+ if ( $theme_name == $item->Name ) {
414
  $theme = $item;
415
  break;
416
  }
417
  }
418
+ if ( null == $theme ) {
419
  return $this->LogError(
420
  'Could not locate theme named "' . $theme . '".',
421
+ array(
422
+ 'ThemeName' => $theme_name,
423
+ 'Themes' => wp_get_themes(),
424
+ )
425
  );
426
  }
427
+ $this->plugin->alerts->Trigger(
428
+ 5006, array(
429
+ 'Theme' => (object) array(
430
+ 'Name' => $theme->Name,
431
+ 'ThemeURI' => $theme->ThemeURI,
432
+ 'Description' => $theme->Description,
433
+ 'Author' => $theme->Author,
434
+ 'Version' => $theme->Version,
435
+ 'get_template_directory' => $theme->get_template_directory(),
436
+ ),
437
+ )
438
+ );
439
  }
440
 
441
  /**
442
  * Plugin creates/modifies posts.
443
  *
444
+ * @param int $post_id - Post ID.
445
  * @param object $post - Post object.
446
  */
447
  public function EventPluginPostCreate( $post_id, $post ) {
448
+ // Filter $_REQUEST array for security.
449
+ $get_array = filter_input_array( INPUT_GET );
450
+ $post_array = filter_input_array( INPUT_POST );
451
+
452
+ $wp_actions = array( 'editpost', 'heartbeat', 'inline-save', 'trash', 'untrash' );
453
+ if ( isset( $get_array['action'] ) && ! in_array( $get_array['action'], $wp_actions ) ) {
454
+ if ( ! in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item', 'customize_changeset', 'custom_css' ) )
455
+ || ! empty( $post->post_title ) ) {
456
+ // If the plugin modify the post.
457
+ if ( false !== strpos( $get_array['action'], 'edit' ) ) {
458
+ $event = $this->GetEventTypeForPostType( $post, 2106, 2107, 2108 );
459
+ $editor_link = $this->GetEditorLink( $post );
460
+ $this->plugin->alerts->Trigger(
461
+ $event, array(
462
+ 'PostID' => $post->ID,
463
+ 'PostType' => $post->post_type,
464
+ 'PostTitle' => $post->post_title,
465
+ $editor_link['name'] => $editor_link['value'],
466
+ )
467
+ );
468
+ } else {
469
+ $event = $this->GetEventTypeForPostType( $post, 5019, 5020, 5021 );
470
+ $this->plugin->alerts->Trigger(
471
+ $event, array(
472
+ 'PostID' => $post->ID,
473
+ 'PostType' => $post->post_type,
474
+ 'PostTitle' => $post->post_title,
475
+ 'Username' => 'Plugins',
476
+ )
477
+ );
478
+ }
479
+ }
480
+ }
481
+
482
+ if ( isset( $post_array['action'] ) && ! in_array( $post_array['action'], $wp_actions ) ) {
483
  if ( ! in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item', 'customize_changeset', 'custom_css' ) )
484
  || ! empty( $post->post_title ) ) {
485
  // If the plugin modify the post.
486
+ if ( false !== strpos( $post_array['action'], 'edit' ) ) {
487
  $event = $this->GetEventTypeForPostType( $post, 2106, 2107, 2108 );
488
+ $editor_link = $this->GetEditorLink( $post );
489
+ $this->plugin->alerts->Trigger(
490
+ $event, array(
491
+ 'PostID' => $post->ID,
492
+ 'PostType' => $post->post_type,
493
+ 'PostTitle' => $post->post_title,
494
+ $editor_link['name'] => $editor_link['value'],
495
+ )
496
+ );
497
  } else {
498
  $event = $this->GetEventTypeForPostType( $post, 5019, 5020, 5021 );
499
+ $this->plugin->alerts->Trigger(
500
+ $event, array(
501
+ 'PostID' => $post->ID,
502
+ 'PostType' => $post->post_type,
503
+ 'PostTitle' => $post->post_title,
504
+ 'Username' => 'Plugins',
505
+ )
506
+ );
507
  }
508
  }
509
  }
515
  * @param integer $post_id - Post ID.
516
  */
517
  public function EventPluginPostDelete( $post_id ) {
518
+ // Filter $_REQUEST array for security.
519
+ $get_array = filter_input_array( INPUT_GET );
520
+ $post_array = filter_input_array( INPUT_POST );
521
+
522
+ if ( empty( $get_array['action'] ) && isset( $get_array['page'] ) ) {
523
  $post = get_post( $post_id );
524
  if ( ! in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item', 'customize_changeset', 'custom_css' ) )
525
  || ! empty( $post->post_title ) ) {
526
  $event = $this->GetEventTypeForPostType( $post, 5025, 5026, 5027 );
527
+ $this->plugin->alerts->Trigger(
528
+ $event, array(
529
+ 'PostID' => $post->ID,
530
+ 'PostType' => $post->post_type,
531
+ 'PostTitle' => $post->post_title,
532
+ 'Username' => 'Plugins',
533
+ )
534
+ );
535
+ }
536
+ }
537
+
538
+ if ( empty( $post_array['action'] ) && isset( $post_array['page'] ) ) {
539
+ $post = get_post( $post_id );
540
+ if ( ! in_array( $post->post_type, array( 'attachment', 'revision', 'nav_menu_item', 'customize_changeset', 'custom_css' ) )
541
+ || ! empty( $post->post_title ) ) {
542
+ $event = $this->GetEventTypeForPostType( $post, 5025, 5026, 5027 );
543
+ $this->plugin->alerts->Trigger(
544
+ $event, array(
545
+ 'PostID' => $post->ID,
546
+ 'PostType' => $post->post_type,
547
+ 'PostTitle' => $post->post_title,
548
+ 'Username' => 'Plugins',
549
+ )
550
+ );
551
  }
552
  }
553
  }
554
 
555
  /**
556
  * Get removed themes.
557
+ *
558
  * @return array of WP_Theme objects
559
  */
560
+ protected function GetRemovedThemes() {
 
561
  $result = $this->old_themes;
562
+ foreach ( $result as $i => $theme ) {
563
+ if ( file_exists( $theme->get_template_directory() ) ) {
564
+ unset( $result[ $i ] );
565
  }
566
  }
567
+ return array_values( $result );
568
  }
569
 
570
  /**
571
  * Get event code by post type.
572
+ *
573
+ * @param object $post - Post object.
574
+ * @param int $type_post - Code for post.
575
+ * @param int $type_page - Code for page.
576
+ * @param int $type_custom - Code for custom post type.
577
  */
578
+ protected function GetEventTypeForPostType( $post, $type_post, $type_page, $type_custom ) {
579
  if ( empty( $post ) || ! isset( $post->post_type ) ) {
580
  return false;
581
  }
582
 
583
  switch ( $post->post_type ) {
584
  case 'page':
585
+ return $type_page;
586
  case 'post':
587
+ return $type_post;
588
  default:
589
+ return $type_custom;
590
  }
591
  }
592
 
594
  * Get editor link.
595
  *
596
  * @param object $post - The post object.
597
+ * @return array $editor_link name and value link.
598
  */
599
  private function GetEditorLink( $post ) {
600
  $name = 'EditorLink';
601
+ $name .= ( 'page' == $post->post_type ) ? 'Page' : 'Post' ;
602
  $value = get_edit_post_link( $post->ID );
603
+ $editor_link = array(
604
+ 'name' => $name,
605
  'value' => $value,
606
  );
607
+ return $editor_link;
608
  }
609
  }
classes/Sensors/Request.php CHANGED
@@ -1,75 +1,116 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Writes the Request.log.php file.
 
 
 
6
  */
7
- class WSAL_Sensors_Request extends WSAL_AbstractSensor
8
- {
9
- protected static $envvars = array();
10
-
11
- /**
12
- * Listening to events using WP hooks.
13
- */
14
- public function HookEvents()
15
- {
16
- if ($this->plugin->settings->IsRequestLoggingEnabled()) {
17
- add_action('shutdown', array($this, 'EventShutdown'));
18
- }
19
- }
20
-
21
- /**
22
- * Fires just before PHP shuts down execution.
23
- */
24
- public function EventShutdown()
25
- {
26
- $upload_dir = wp_upload_dir();
27
- $uploadsDirPath = trailingslashit($upload_dir['basedir']).'wp-security-audit-log/';
28
- if (!$this->CheckDirectory($uploadsDirPath)) {
29
- wp_mkdir_p($uploadsDirPath);
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
- $file = $uploadsDirPath . 'Request.log.php';
33
-
34
- $line = '['.date('Y-m-d H:i:s').'] '
35
- . $_SERVER['REQUEST_METHOD'] . ' '
36
- . $_SERVER['REQUEST_URI'] . ' '
37
- . (!empty($_POST) ? str_pad(PHP_EOL, 24) . json_encode($_POST) : '')
38
- . (!empty(self::$envvars) ? str_pad(PHP_EOL, 24) . json_encode(self::$envvars) : '')
39
- . PHP_EOL;
40
-
41
- if (!file_exists($file) && !file_put_contents($file, '<'.'?php die(\'Access Denied\'); ?>' . PHP_EOL)) {
42
- return $this->LogError('Could not initialize request log file', array('file' => $file));
43
- }
44
-
45
- $f = fopen($file, 'a');
46
- if ($f) {
47
- if (!fwrite($f, $line)) {
48
- $this->LogWarn('Could not write to log file', array('file' => $file));
49
- }
50
- if (!fclose($f)) {
51
- $this->LogWarn('Could not close log file', array('file' => $file));
52
- }
53
- } else {
54
- $this->LogWarn('Could not open log file', array('file' => $file));
55
- }
56
- }
57
-
58
- /**
59
- * Sets $envvars element with key and value.
60
- */
61
- public static function SetVar($name, $value)
62
- {
63
- self::$envvars[$name] = $value;
64
- }
65
-
66
- /**
67
- * Copy data array into $envvars array.
68
- */
69
- public static function SetVars($data)
70
- {
71
- foreach ($data as $name => $value) {
72
- self::SetVar($name, $value);
73
- }
74
- }
75
  }
1
  <?php
2
  /**
3
+ * Sensor: Request
4
+ *
5
+ * Request sensor class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Writes the Request.log.php file.
18
+ *
19
+ * @package Wsal
20
+ * @subpackage Sensors
21
  */
22
+ class WSAL_Sensors_Request extends WSAL_AbstractSensor {
23
+
24
+ /**
25
+ * Environment Variables
26
+ *
27
+ * @var array
28
+ */
29
+ protected static $envvars = array();
30
+
31
+ /**
32
+ * Listening to events using WP hooks.
33
+ */
34
+ public function HookEvents() {
35
+ if ( $this->plugin->settings->IsRequestLoggingEnabled() ) {
36
+ add_action( 'shutdown', array( $this, 'EventShutdown' ) );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Fires just before PHP shuts down execution.
42
+ */
43
+ public function EventShutdown() {
44
+ // Filter global arrays for security.
45
+ $post_array = filter_input_array( INPUT_POST );
46
+ $server_array = filter_input_array( INPUT_SERVER );
47
+
48
+ $upload_dir = wp_upload_dir();
49
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/';
50
+ if ( ! $this->CheckDirectory( $uploads_dir_path ) ) {
51
+ wp_mkdir_p( $uploads_dir_path );
52
+ }
53
+
54
+ $file = $uploads_dir_path . 'Request.log.php';
55
+
56
+ $line = '[' . date( 'Y-m-d H:i:s' ) . '] '
57
+ . $server_array['REQUEST_METHOD'] . ' '
58
+ . $server_array['REQUEST_URI'] . ' '
59
+ . ( ! empty( $post_array ) ? str_pad( PHP_EOL, 24 ) . json_encode( $post_array ) : '')
60
+ . ( ! empty( self::$envvars ) ? str_pad( PHP_EOL, 24 ) . json_encode( self::$envvars ) : '')
61
+ . PHP_EOL;
62
+
63
+ if ( ! file_exists( $file ) && ! file_put_contents( $file, '<' . '?php die(\'Access Denied\'); ?>' . PHP_EOL ) ) {
64
+ return $this->LogError(
65
+ 'Could not initialize request log file', array(
66
+ 'file' => $file,
67
+ )
68
+ );
69
+ }
70
+
71
+ $f = fopen( $file, 'a' );
72
+ if ( $f ) {
73
+ if ( ! fwrite( $f, $line ) ) {
74
+ $this->LogWarn(
75
+ 'Could not write to log file', array(
76
+ 'file' => $file,
77
+ )
78
+ );
79
+ }
80
+ if ( ! fclose( $f ) ) {
81
+ $this->LogWarn(
82
+ 'Could not close log file', array(
83
+ 'file' => $file,
84
+ )
85
+ );
86
+ }
87
+ } else {
88
+ $this->LogWarn(
89
+ 'Could not open log file', array(
90
+ 'file' => $file,
91
+ )
92
+ );
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Sets $envvars element with key and value.
98
+ *
99
+ * @param mixed $name - Key name of the variable.
100
+ * @param mixed $value - Value of the variable.
101
+ */
102
+ public static function SetVar( $name, $value ) {
103
+ self::$envvars[ $name ] = $value;
104
+ }
105
 
106
+ /**
107
+ * Copy data array into $envvars array.
108
+ *
109
+ * @param array $data - Data array.
110
+ */
111
+ public static function SetVars( $data ) {
112
+ foreach ( $data as $name => $value ) {
113
+ self::SetVar( $name, $value );
114
+ }
115
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
classes/Sensors/System.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * System Activity sensor.
6
  *
7
  * 6000 Events automatically pruned by system
@@ -28,730 +40,914 @@
28
  * 6016 Changed the number of links that a comment must have to be held in the queue
29
  * 6017 Modified the list of keywords for comments moderation
30
  * 6018 Modified the list of keywords for comments blacklisting
 
 
 
31
  */
32
  class WSAL_Sensors_System extends WSAL_AbstractSensor {
33
 
34
- /**
35
- * Transient name.
36
- * WordPress will prefix the name with "_transient_" or "_transient_timeout_" in the options table.
37
- */
38
- const TRANSIENT_404 = 'wsal-404-attempts';
39
- const TRANSIENT_VISITOR_404 = 'wsal-visitor-404-attempts';
40
-
41
- /**
42
- * Listening to events using WP hooks.
43
- */
44
- public function HookEvents() {
45
-
46
- add_action( 'wsal_prune', array( $this, 'EventPruneEvents' ), 10, 2 );
47
- add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
48
-
49
- add_action( 'automatic_updates_complete', array( $this, 'WPUpdate' ), 10, 1 );
50
- add_filter( 'template_redirect', array( $this, 'Event404' ) );
51
-
52
- $upload_dir = wp_upload_dir();
53
- $uploadsDirPath = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
54
- if ( ! $this->CheckDirectory( $uploadsDirPath ) ) {
55
- wp_mkdir_p( $uploadsDirPath );
56
- }
57
-
58
- // Directory for logged in users log files.
59
- $user_upload_dir = wp_upload_dir();
60
- $user_upload_path = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/404s/users/' );
61
- if ( ! $this->CheckDirectory( $user_upload_path ) ) {
62
- wp_mkdir_p( $user_upload_path );
63
- }
64
-
65
- // Directory for visitor log files.
66
- $visitor_upload_dir = wp_upload_dir();
67
- $visitor_upload_path = trailingslashit( $visitor_upload_dir['basedir'] . '/wp-security-audit-log/404s/visitors/' );
68
- if ( ! $this->CheckDirectory( $visitor_upload_path ) ) {
69
- wp_mkdir_p( $visitor_upload_path );
70
- }
71
-
72
- // Cron Job 404 log files pruning.
73
- add_action( 'log_files_pruning', array( $this, 'LogFilesPruning' ) );
74
- if ( ! wp_next_scheduled( 'log_files_pruning' ) ) {
75
- wp_schedule_event( time(), 'daily', 'log_files_pruning' );
76
- }
77
- // whitelist options.
78
- add_action( 'whitelist_options', array( $this, 'EventOptions' ), 10, 1 );
79
- }
80
-
81
- /**
82
- * @param int $count The number of deleted events.
83
- * @param string $query Query that selected events for deletion.
84
- */
85
- public function EventPruneEvents($count, $query)
86
- {
87
- $this->plugin->alerts->Trigger(6000, array(
88
- 'EventCount' => $count,
89
- 'PruneQuery' => $query,
90
- ));
91
- }
92
-
93
- /**
94
- * 404 limit count.
95
- * @return integer limit
96
- */
97
- protected function Get404LogLimit()
98
- {
99
- return $this->plugin->settings->Get404LogLimit();
100
- }
101
-
102
- protected function GetVisitor404LogLimit() {
103
- return $this->plugin->settings->GetVisitor404LogLimit();
104
- }
105
-
106
- /**
107
- * Expiration of the transient saved in the WP database.
108
- * @return integer Time until expiration in seconds from now
109
- */
110
- protected function Get404Expiration()
111
- {
112
- return 24 * 60 * 60;
113
- }
114
-
115
- /**
116
- * Check 404 limit.
117
- * @param integer $site_id blog ID
118
- * @param string $username username
119
- * @param string $ip IP address
120
- * @return boolean passed limit true|false
121
- */
122
- protected function IsPast404Limit($site_id, $username, $ip)
123
- {
124
- $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
125
- $data = $get_fn(self::TRANSIENT_404);
126
- return ($data !== false) && isset($data[$site_id.":".$username.":".$ip]) && ($data[$site_id.":".$username.":".$ip] > $this->Get404LogLimit());
127
- }
128
-
129
- protected function IsPastVisitor404Limit( $site_id, $username, $ip ) {
130
- $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
131
- $data = $get_fn( self::TRANSIENT_VISITOR_404 );
132
- return ( false !== $data ) && isset( $data[ $site_id . ':' . $username . ':' . $ip ] ) && ( $data[ $site_id . ':' . $username . ':' . $ip ] > $this->GetVisitor404LogLimit() );
133
- }
134
-
135
- /**
136
- * Increment 404 limit.
137
- * @param integer $site_id blog ID
138
- * @param string $username username
139
- * @param string $ip IP address
140
- */
141
- protected function Increment404($site_id, $username, $ip) {
142
- $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
143
- $set_fn = $this->IsMultisite() ? 'set_site_transient' : 'set_transient';
144
-
145
- $data = $get_fn(self::TRANSIENT_404);
146
- if (!$data) {
147
- $data = array();
148
- }
149
- if (!isset($data[$site_id.":".$username.":".$ip])) {
150
- $data[$site_id.":".$username.":".$ip] = 1;
151
- }
152
- $data[$site_id.":".$username.":".$ip]++;
153
- $set_fn(self::TRANSIENT_404, $data, $this->Get404Expiration());
154
- }
155
-
156
- protected function IncrementVisitor404( $site_id, $username, $ip ) {
157
- $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
158
- $set_fn = $this->IsMultisite() ? 'set_site_transient' : 'set_transient';
159
-
160
- $data = $get_fn( self::TRANSIENT_VISITOR_404 );
161
- if ( ! $data ) {
162
- $data = array();
163
- }
164
- if ( ! isset( $data[ $site_id . ':' . $username . ':' . $ip ] ) ) {
165
- $data[ $site_id . ':' . $username . ':' . $ip ] = 1;
166
- }
167
- $data[ $site_id . ':' . $username . ':' . $ip ]++;
168
- $set_fn( self::TRANSIENT_VISITOR_404, $data, $this->Get404Expiration() );
169
- }
170
-
171
- /**
172
- * Event 404 Not found.
173
- */
174
- public function Event404() {
175
-
176
- $attempts = 1;
177
-
178
- global $wp_query;
179
- if ( ! $wp_query->is_404 ) {
180
- return;
181
- }
182
- $msg = 'times';
183
-
184
- list( $y, $m, $d ) = explode( '-', date( 'Y-m-d' ) );
185
-
186
- $site_id = ( function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 0 );
187
- $ip = $this->plugin->settings->GetMainClientIP();
188
-
189
- if ( ! is_user_logged_in() ) {
190
- $username = 'Website Visitor';
191
- } else {
192
- $username = wp_get_current_user()->user_login;
193
- }
194
-
195
- if ( 'Website Visitor' !== $username ) {
196
-
197
- // Check if the alert is disabled from the "Enable/Disable Alerts" section.
198
- if ( ! $this->plugin->alerts->IsEnabled( 6007 ) ) {
199
- return;
200
- }
201
-
202
- if ( $this->IsPast404Limit( $site_id, $username, $ip ) ) {
203
- return;
204
- }
205
-
206
- $objOcc = new WSAL_Models_Occurrence();
207
-
208
- $occ = $objOcc->CheckAlert404(
209
- array(
210
- $ip,
211
- $username,
212
- 6007,
213
- $site_id,
214
- mktime(0, 0, 0, $m, $d, $y),
215
- mktime(0, 0, 0, $m, $d + 1, $y) - 1
216
- )
217
- );
218
-
219
- $occ = count( $occ ) ? $occ[0] : null;
220
- if ( ! empty( $occ ) ) {
221
- // update existing record.
222
- $this->Increment404($site_id, $username, $ip);
223
- $new = $occ->GetMetaValue('Attempts', 0) + 1;
224
-
225
- if ($new > $this->Get404LogLimit()) {
226
- $new = 'more than ' . $this->Get404LogLimit();
227
- $msg .= ' This could possible be a scan, therefore keep an eye on the activity from this IP Address';
228
- }
229
-
230
- $linkFile = $this->WriteLog( $new, $ip, $username );
231
-
232
- $occ->UpdateMetaValue('Attempts', $new);
233
- $occ->UpdateMetaValue('Username', $username);
234
- $occ->UpdateMetaValue('Msg', $msg);
235
- if (!empty($linkFile)) {
236
- $occ->UpdateMetaValue('LinkFile', $linkFile);
237
- }
238
- $occ->created_on = null;
239
- $occ->Save();
240
- } else {
241
- $linkFile = $this->WriteLog( 1, $ip, $username );
242
- // create a new record
243
- $fields = array(
244
- 'Attempts' => 1,
245
- 'Username' => $username,
246
- 'Msg' => $msg
247
- );
248
- if (!empty($linkFile)) {
249
- $fields['LinkFile'] = $linkFile;
250
- }
251
- $this->plugin->alerts->Trigger(6007, $fields);
252
- }
253
- } else {
254
-
255
- // Check if the alert is disabled from the "Enable/Disable Alerts" section.
256
- if ( ! $this->plugin->alerts->IsEnabled( 6023 ) ) {
257
- return;
258
- }
259
-
260
- if ( $this->IsPastVisitor404Limit( $site_id, $username, $ip ) ) {
261
- return;
262
- }
263
-
264
- $objOcc = new WSAL_Models_Occurrence();
265
-
266
- $occ = $objOcc->CheckAlert404(
267
- array(
268
- $ip,
269
- $username,
270
- 6023,
271
- $site_id,
272
- mktime(0, 0, 0, $m, $d, $y),
273
- mktime(0, 0, 0, $m, $d + 1, $y) - 1
274
- )
275
- );
276
-
277
- $occ = count( $occ ) ? $occ[0] : null;
278
- if ( ! empty( $occ ) ) {
279
- // update existing record.
280
- $this->IncrementVisitor404( $site_id, $username, $ip );
281
- $new = $occ->GetMetaValue( 'Attempts', 0 ) + 1;
282
-
283
- if ( $new > $this->GetVisitor404LogLimit() ) {
284
- $new = 'more than ' . $this->GetVisitor404LogLimit();
285
- $msg .= ' This could possible be a scan, therefore keep an eye on the activity from this IP Address';
286
- }
287
-
288
- $linkFile = $this->WriteLog( $new, $ip, $username, false );
289
-
290
- $occ->UpdateMetaValue( 'Attempts', $new );
291
- $occ->UpdateMetaValue( 'Username', $username );
292
- $occ->UpdateMetaValue( 'Msg', $msg );
293
- if ( ! empty( $linkFile ) ) {
294
- $occ->UpdateMetaValue( 'LinkFile', $linkFile );
295
- }
296
- $occ->created_on = null;
297
- $occ->Save();
298
- } else {
299
- $linkFile = $this->WriteLog( 1, $ip, $username, false );
300
- // create a new record.
301
- $fields = array(
302
- 'Attempts' => 1,
303
- 'Username' => $username,
304
- 'Msg' => $msg,
305
- );
306
- if ( ! empty( $linkFile ) ) {
307
- $fields['LinkFile'] = $linkFile;
308
- }
309
- $this->plugin->alerts->Trigger( 6023, $fields );
310
- }
311
- }
312
- }
313
-
314
- /**
315
- * Triggered when a user accesses the admin area.
316
- */
317
- public function EventAdminInit() {
318
-
319
- // Destroy all the session of the same user from user profile page.
320
- if ( isset( $_REQUEST['action'] ) && ( 'destroy-sessions' == $_REQUEST['action'] ) && isset( $_REQUEST['user_id'] ) ) {
321
- $this->plugin->alerts->Trigger( 1006, array(
322
- 'TargetUserID' => $_REQUEST['user_id'],
323
- ) );
324
- }
325
-
326
- // Make sure user can actually modify target options.
327
- if ( ! current_user_can( 'manage_options' ) ) {
328
- return;
329
- }
330
- $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
331
- $actype = basename($_SERVER['SCRIPT_NAME'], '.php');
332
- $is_option_page = $actype == 'options';
333
- $is_network_settings = $actype == 'settings';
334
- $is_permalink_page = $actype == 'options-permalink';
335
-
336
- if ($is_option_page && (get_option('users_can_register') xor isset($_POST['users_can_register']))) {
337
- $old = get_option('users_can_register') ? 'Enabled' : 'Disabled';
338
- $new = isset($_POST['users_can_register']) ? 'Enabled' : 'Disabled';
339
- if ($old != $new) {
340
- $this->plugin->alerts->Trigger(6001, array(
341
- 'OldValue' => $old,
342
- 'NewValue' => $new,
343
- 'CurrentUserID' => wp_get_current_user()->ID,
344
- ));
345
- }
346
- }
347
-
348
- if ($is_option_page && !empty($_POST['default_role'])) {
349
- $old = get_option('default_role');
350
- $new = trim($_POST['default_role']);
351
- if ($old != $new) {
352
- $this->plugin->alerts->Trigger(6002, array(
353
- 'OldRole' => $old,
354
- 'NewRole' => $new,
355
- 'CurrentUserID' => wp_get_current_user()->ID,
356
- ));
357
- }
358
- }
359
-
360
- if ($is_option_page && !empty($_POST['admin_email'])) {
361
- $old = get_option('admin_email');
362
- $new = trim($_POST['admin_email']);
363
- if ($old != $new) {
364
- $this->plugin->alerts->Trigger(6003, array(
365
- 'OldEmail' => $old,
366
- 'NewEmail' => $new,
367
- 'CurrentUserID' => wp_get_current_user()->ID,
368
- ));
369
- }
370
- }
371
-
372
- if ($is_network_settings && !empty($_POST['admin_email'])) {
373
- $old = get_site_option('admin_email');
374
- $new = trim($_POST['admin_email']);
375
- if ($old != $new) {
376
- $this->plugin->alerts->Trigger(6003, array(
377
- 'OldEmail' => $old,
378
- 'NewEmail' => $new,
379
- 'CurrentUserID' => wp_get_current_user()->ID,
380
- ));
381
- }
382
- }
383
-
384
- if ($is_permalink_page && !empty($_REQUEST['permalink_structure'])) {
385
- $old = get_option('permalink_structure');
386
- $new = trim($_REQUEST['permalink_structure']);
387
- if ($old != $new) {
388
- $this->plugin->alerts->Trigger(6005, array(
389
- 'OldPattern' => $old,
390
- 'NewPattern' => $new,
391
- 'CurrentUserID' => wp_get_current_user()->ID,
392
- ));
393
- }
394
- }
395
-
396
- if ($action == 'do-core-upgrade' && isset($_REQUEST['version'])) {
397
- $oldVersion = get_bloginfo('version');
398
- $newVersion = $_REQUEST['version'];
399
- if ($oldVersion != $newVersion) {
400
- $this->plugin->alerts->Trigger(6004, array(
401
- 'OldVersion' => $oldVersion,
402
- 'NewVersion' => $newVersion,
403
- ));
404
- }
405
- }
406
-
407
- /* BBPress Forum support Setting */
408
- if ($action == 'update' && isset($_REQUEST['_bbp_default_role'])) {
409
- $oldRole = get_option('_bbp_default_role');
410
- $newRole = $_REQUEST['_bbp_default_role'];
411
- if ($oldRole != $newRole) {
412
- $this->plugin->alerts->Trigger(8009, array(
413
- 'OldRole' => $oldRole,
414
- 'NewRole' => $newRole
415
- ));
416
- }
417
- }
418
-
419
- if ($action == 'update' && isset($_REQUEST['option_page']) && ($_REQUEST['option_page'] == 'bbpress')) {
420
- // Anonymous posting
421
- $allow_anonymous = get_option('_bbp_allow_anonymous');
422
- $oldStatus = !empty($allow_anonymous) ? 1 : 0;
423
- $newStatus = !empty($_REQUEST['_bbp_allow_anonymous']) ? 1 : 0;
424
- if ($oldStatus != $newStatus) {
425
- $status = ($newStatus == 1) ? 'Enabled' : 'Disabled';
426
- $this->plugin->alerts->Trigger(8010, array(
427
- 'Status' => $status
428
- ));
429
- }
430
- // Disallow editing after
431
- $bbp_edit_lock = get_option('_bbp_edit_lock');
432
- $oldTime = !empty($bbp_edit_lock) ? $bbp_edit_lock : '';
433
- $newTime = !empty($_REQUEST['_bbp_edit_lock']) ? $_REQUEST['_bbp_edit_lock'] : '';
434
- if ($oldTime != $newTime) {
435
- $this->plugin->alerts->Trigger(8012, array(
436
- 'OldTime' => $oldTime,
437
- 'NewTime' => $newTime
438
- ));
439
- }
440
- // Throttle posting every
441
- $bbp_throttle_time = get_option('_bbp_throttle_time');
442
- $oldTime2 = !empty($bbp_throttle_time) ? $bbp_throttle_time : '';
443
- $newTime2 = !empty($_REQUEST['_bbp_throttle_time']) ? $_REQUEST['_bbp_throttle_time'] : '';
444
- if ($oldTime2 != $newTime2) {
445
- $this->plugin->alerts->Trigger(8013, array(
446
- 'OldTime' => $oldTime2,
447
- 'NewTime' => $newTime2
448
- ));
449
- }
450
- }
451
- }
452
-
453
- /**
454
- * WordPress auto core update
455
- */
456
- public function WPUpdate($automatic)
457
- {
458
- if (isset($automatic['core'][0])) {
459
- $obj = $automatic['core'][0];
460
- $oldVersion = get_bloginfo('version');
461
- $this->plugin->alerts->Trigger(6004, array(
462
- 'OldVersion' => $oldVersion,
463
- 'NewVersion' => $obj->item->version.' (auto update)'
464
- ));
465
- }
466
- }
467
-
468
- /**
469
- * Purge log files older than one month.
470
- */
471
- public function LogFilesPruning() {
472
- if ($this->plugin->GetGlobalOption('purge-404-log', 'off') == 'on') {
473
- $upload_dir = wp_upload_dir();
474
- $uploadsDirPath = trailingslashit($upload_dir['basedir']).'wp-security-audit-log/404s/users/';
475
- if (is_dir($uploadsDirPath)) {
476
- if ($handle = opendir($uploadsDirPath)) {
477
- while (false !== ($entry = readdir($handle))) {
478
- if ($entry != "." && $entry != "..") {
479
- if (file_exists($uploadsDirPath . $entry)) {
480
- $modified = filemtime($uploadsDirPath . $entry);
481
- if ($modified < strtotime('-4 weeks')) {
482
- // Delete file
483
- unlink($uploadsDirPath . $entry);
484
- }
485
- }
486
- }
487
- }
488
- closedir($handle);
489
- }
490
- }
491
- }
492
- if ( 'on' == $this->plugin->GetGlobalOption( 'purge-visitor-404-log', 'off' ) ) {
493
- $upload_dir = wp_upload_dir();
494
- $uploadsDirPath = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/visitors/';
495
- if ( is_dir( $uploadsDirPath ) ) {
496
- if ( $handle = opendir( $uploadsDirPath ) ) {
497
- while ( false !== ( $entry = readdir( $handle ) ) ) {
498
- if ( $entry != "." && $entry != ".." ) {
499
- if ( file_exists( $uploadsDirPath . $entry ) ) {
500
- $modified = filemtime( $uploadsDirPath . $entry );
501
- if ( $modified < strtotime( '-4 weeks' ) ) {
502
- // Delete file.
503
- unlink( $uploadsDirPath . $entry );
504
- }
505
- }
506
- }
507
- }
508
- closedir( $handle );
509
- }
510
- }
511
- }
512
- }
513
-
514
- /**
515
- * Events from 6008 to 6018
516
- */
517
- public function EventOptions($whitelist = null)
518
- {
519
- if (isset($_REQUEST['option_page']) && $_REQUEST['option_page'] == "reading") {
520
- $old_status = get_option('blog_public', 1);
521
- $new_status = isset($_REQUEST['blog_public']) ? 0 : 1;
522
- if ($old_status != $new_status) {
523
- $this->plugin->alerts->Trigger(6008, array(
524
- 'Status' => ($new_status == 0) ? 'Enabled' : 'Disabled'
525
- ));
526
- }
527
- }
528
- if (isset($_REQUEST['option_page']) && $_REQUEST['option_page'] == "discussion") {
529
- $old_status = get_option('default_comment_status', 'closed');
530
- $new_status = isset($_REQUEST['default_comment_status']) ? 'open' : 'closed';
531
- if ($old_status != $new_status) {
532
- $this->plugin->alerts->Trigger(6009, array(
533
- 'Status' => ($new_status == 'open') ? 'Enabled' : 'Disabled'
534
- ));
535
- }
536
- $old_status = get_option('require_name_email', 0);
537
- $new_status = isset($_REQUEST['require_name_email']) ? 1 : 0;
538
- if ($old_status != $new_status) {
539
- $this->plugin->alerts->Trigger(6010, array(
540
- 'Status' => ($new_status == 1) ? 'Enabled' : 'Disabled'
541
- ));
542
- }
543
- $old_status = get_option('comment_registration', 0);
544
- $new_status = isset($_REQUEST['comment_registration']) ? 1 : 0;
545
- if ($old_status != $new_status) {
546
- $this->plugin->alerts->Trigger(6011, array(
547
- 'Status' => ($new_status == 1) ? 'Enabled' : 'Disabled'
548
- ));
549
- }
550
- $old_status = get_option('close_comments_for_old_posts', 0);
551
- $new_status = isset($_REQUEST['close_comments_for_old_posts']) ? 1 : 0;
552
- if ($old_status != $new_status) {
553
- $value = isset($_REQUEST['close_comments_days_old']) ? $_REQUEST['close_comments_days_old'] : 0;
554
- $this->plugin->alerts->Trigger(6012, array(
555
- 'Status' => ($new_status == 1) ? 'Enabled' : 'Disabled',
556
- 'Value' => $value
557
- ));
558
- }
559
- $old_value = get_option('close_comments_days_old', 0);
560
- $new_value = isset($_REQUEST['close_comments_days_old']) ? $_REQUEST['close_comments_days_old'] : 0;
561
- if ($old_value != $new_value) {
562
- $this->plugin->alerts->Trigger(6013, array(
563
- 'OldValue' => $old_value,
564
- 'NewValue' => $new_value
565
- ));
566
- }
567
- $old_status = get_option('comment_moderation', 0);
568
- $new_status = isset($_REQUEST['comment_moderation']) ? 1 : 0;
569
- if ($old_status != $new_status) {
570
- $this->plugin->alerts->Trigger(6014, array(
571
- 'Status' => ($new_status == 1) ? 'Enabled' : 'Disabled'
572
- ));
573
- }
574
- $old_status = get_option('comment_whitelist', 0);
575
- $new_status = isset($_REQUEST['comment_whitelist']) ? 1 : 0;
576
- if ($old_status != $new_status) {
577
- $this->plugin->alerts->Trigger(6015, array(
578
- 'Status' => ($new_status == 1) ? 'Enabled' : 'Disabled'
579
- ));
580
- }
581
- $old_value = get_option('comment_max_links', 0);
582
- $new_value = isset($_REQUEST['comment_max_links']) ? $_REQUEST['comment_max_links'] : 0;
583
- if ($old_value != $new_value) {
584
- $this->plugin->alerts->Trigger(6016, array(
585
- 'OldValue' => $old_value,
586
- 'NewValue' => $new_value
587
- ));
588
- }
589
- $old_value = get_option('moderation_keys', 0);
590
- $new_value = isset($_REQUEST['moderation_keys']) ? $_REQUEST['moderation_keys'] : 0;
591
- if ($old_value != $new_value) {
592
- $this->plugin->alerts->Trigger(6017, array());
593
- }
594
- $old_value = get_option('blacklist_keys', 0);
595
- $new_value = isset($_REQUEST['blacklist_keys']) ? $_REQUEST['blacklist_keys'] : 0;
596
- if ($old_value != $new_value) {
597
- $this->plugin->alerts->Trigger(6018, array());
598
- }
599
- }
600
- return $whitelist;
601
- }
602
-
603
- /**
604
- * Write a new line on 404 log file
605
- * Folder: /uploads/wp-security-audit-log/404s/
606
- */
607
- private function WriteLog( $attempts, $ip, $username = '', $logged_in = true ) {
608
- $nameFile = null;
609
-
610
- if ( $logged_in ) {
611
- if ( 'on' == $this->plugin->GetGlobalOption( 'log-404', 'off' ) ) {
612
- // Request URL.
613
- $url = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
614
-
615
- // Get option to log referrer.
616
- $log_referrer = $this->plugin->GetGlobalOption( 'log-404-referrer' );
617
-
618
- if ( 'on' === $log_referrer ) {
619
- // Get the referer.
620
- $referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ) ? $_SERVER['HTTP_REFERER'] : false;
621
- // Create/Append to the log file.
622
- $data = 'Request URL ' . $url . ' Referer ' . $referrer . ',';
623
- } else {
624
- // Create/Append to the log file.
625
- $data = 'Request URL ' . $url . ',';
626
- }
627
-
628
- if ( ! is_user_logged_in() ) {
629
- $username = '';
630
- } else {
631
- $username = $username . '_';
632
- }
633
-
634
- if ( '127.0.0.1' == $ip || '::1' == $ip ) {
635
- $ip = 'localhost';
636
- }
637
- $upload_dir = wp_upload_dir();
638
- $uploadsDirPath = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/users/';
639
- $uploadsURL = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/users/';
640
-
641
- // Check directory.
642
- if ( $this->CheckDirectory( $uploadsDirPath ) ) {
643
- $filename = '6007_' . date( 'Ymd' ) . '.log';
644
- $fp = $uploadsDirPath . $filename;
645
- $nameFile = $uploadsURL . $filename;
646
- if ( ! $file = fopen( $fp, 'a' ) ) {
647
- $i = 1;
648
- $fileOpened = false;
649
- do {
650
- $fp2 = substr( $fp, 0, -4 ) . '_' . $i . '.log';
651
- if ( ! file_exists( $fp2 ) ) {
652
- if ( $file = fopen( $fp2, 'a' ) ) {
653
- $fileOpened = true;
654
- $nameFile = $uploadsURL . substr( $nameFile, 0, -4 ) . '_' . $i . '.log';
655
- }
656
- } else {
657
- $latestFilename = $this->GetLastModified( $uploadsDirPath, $filename );
658
- $fpLast = $uploadsDirPath . $latestFilename;
659
- if ( $file = fopen( $fpLast, 'a' ) ) {
660
- $fileOpened = true;
661
- $nameFile = $uploadsURL . $latestFilename;
662
- }
663
- }
664
- $i++;
665
- } while ( ! $fileOpened );
666
- }
667
- fwrite( $file, sprintf( "%s\n", $data ) );
668
- fclose( $file );
669
- }
670
- }
671
- } else {
672
- if ( 'on' == $this->plugin->GetGlobalOption( 'log-visitor-404', 'off' ) ) {
673
- // Request URL.
674
- $url = $_SERVER["HTTP_HOST"] . $_SERVER['REQUEST_URI'];
675
-
676
- // Get option to log referrer.
677
- $log_referrer = $this->plugin->GetGlobalOption( 'log-visitor-404-referrer' );
678
-
679
- if ( 'on' === $log_referrer ) {
680
- // Get the referer.
681
- $referrer = ( isset( $_SERVER['HTTP_REFERER'] ) ) ? $_SERVER['HTTP_REFERER'] : false;
682
- // Create/Append to the log file.
683
- $data = 'Request URL ' . $url . ' Referer ' . $referrer . ',';
684
- } else {
685
- // Create/Append to the log file.
686
- $data = 'Request URL ' . $url . ',';
687
- }
688
-
689
- $username = '';
690
-
691
- if ( '127.0.0.1' == $ip || '::1' == $ip ) {
692
- $ip = 'localhost';
693
- }
694
- $upload_dir = wp_upload_dir();
695
- $uploadsDirPath = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/visitors/';
696
- $uploadsURL = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/visitors/';
697
-
698
- // Check directory.
699
- if ( $this->CheckDirectory( $uploadsDirPath ) ) {
700
- $filename = '6023_' . date( 'Ymd' ) . '.log';
701
- $fp = $uploadsDirPath . $filename;
702
- $nameFile = $uploadsURL . $filename;
703
- if ( ! $file = fopen( $fp, 'a' ) ) {
704
- $i = 1;
705
- $fileOpened = false;
706
- do {
707
- $fp2 = substr( $fp, 0, -4 ) . '_' . $i . '.log';
708
- if ( ! file_exists( $fp2 ) ) {
709
- if ( $file = fopen( $fp2, 'a' ) ) {
710
- $fileOpened = true;
711
- $nameFile = $uploadsURL . substr( $nameFile, 0, -4 ) . '_' . $i . '.log';
712
- }
713
- } else {
714
- $latestFilename = $this->GetLastModified( $uploadsDirPath, $filename );
715
- $fpLast = $uploadsDirPath . $latestFilename;
716
- if ( $file = fopen( $fpLast, 'a' ) ) {
717
- $fileOpened = true;
718
- $nameFile = $uploadsURL . $latestFilename;
719
- }
720
- }
721
- $i++;
722
- } while ( ! $fileOpened );
723
- }
724
- fwrite( $file, sprintf( "%s\n", $data ) );
725
- fclose( $file );
726
- }
727
- }
728
- }
729
-
730
- return $nameFile;
731
- }
732
-
733
- /**
734
- * Get the latest file modified.
735
- * @return string $latest_filename file name
736
- */
737
- private function GetLastModified($uploadsDirPath, $filename)
738
- {
739
- $filename = substr($filename, 0, -4);
740
- $latest_mtime = 0;
741
- $latest_filename = '';
742
- if ($handle = opendir($uploadsDirPath)) {
743
- while (false !== ($entry = readdir($handle))) {
744
- if ($entry != "." && $entry != "..") {
745
- if (preg_match('/^'.$filename.'/i', $entry) > 0) {
746
- if (filemtime($uploadsDirPath . $entry) > $latest_mtime) {
747
- $latest_mtime = filemtime($uploadsDirPath . $entry);
748
- $latest_filename = $entry;
749
- }
750
- }
751
- }
752
- }
753
- closedir($handle);
754
- }
755
- return $latest_filename;
756
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
757
  }
1
  <?php
2
  /**
3
+ * Sensor: System Activity
4
+ *
5
+ * System activity sensor class file.
6
+ *
7
+ * @since 1.0.0
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
  * 6000 Events automatically pruned by system
40
  * 6016 Changed the number of links that a comment must have to be held in the queue
41
  * 6017 Modified the list of keywords for comments moderation
42
  * 6018 Modified the list of keywords for comments blacklisting
43
+ *
44
+ * @package Wsal
45
+ * @subpackage Sensors
46
  */
47
  class WSAL_Sensors_System extends WSAL_AbstractSensor {
48
 
49
+ /**
50
+ * Transient name.
51
+ * WordPress will prefix the name with "_transient_"
52
+ * or "_transient_timeout_" in the options table.
53
+ */
54
+ const TRANSIENT_404 = 'wsal-404-attempts';
55
+ const TRANSIENT_VISITOR_404 = 'wsal-visitor-404-attempts';
56
+
57
+ /**
58
+ * Listening to events using WP hooks.
59
+ */
60
+ public function HookEvents() {
61
+
62
+ add_action( 'wsal_prune', array( $this, 'EventPruneEvents' ), 10, 2 );
63
+ add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
64
+
65
+ add_action( 'automatic_updates_complete', array( $this, 'WPUpdate' ), 10, 1 );
66
+ add_filter( 'template_redirect', array( $this, 'Event404' ) );
67
+
68
+ $upload_dir = wp_upload_dir();
69
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/';
70
+ if ( ! $this->CheckDirectory( $uploads_dir_path ) ) {
71
+ wp_mkdir_p( $uploads_dir_path );
72
+ }
73
+
74
+ // Directory for logged in users log files.
75
+ $user_upload_dir = wp_upload_dir();
76
+ $user_upload_path = trailingslashit( $user_upload_dir['basedir'] . '/wp-security-audit-log/404s/users/' );
77
+ if ( ! $this->CheckDirectory( $user_upload_path ) ) {
78
+ wp_mkdir_p( $user_upload_path );
79
+ }
80
+
81
+ // Directory for visitor log files.
82
+ $visitor_upload_dir = wp_upload_dir();
83
+ $visitor_upload_path = trailingslashit( $visitor_upload_dir['basedir'] . '/wp-security-audit-log/404s/visitors/' );
84
+ if ( ! $this->CheckDirectory( $visitor_upload_path ) ) {
85
+ wp_mkdir_p( $visitor_upload_path );
86
+ }
87
+
88
+ // Cron Job 404 log files pruning.
89
+ add_action( 'log_files_pruning', array( $this, 'LogFilesPruning' ) );
90
+ if ( ! wp_next_scheduled( 'log_files_pruning' ) ) {
91
+ wp_schedule_event( time(), 'daily', 'log_files_pruning' );
92
+ }
93
+ // whitelist options.
94
+ add_action( 'whitelist_options', array( $this, 'EventOptions' ), 10, 1 );
95
+
96
+ // Update admin email alert.
97
+ add_action( 'update_option_admin_email', array( $this, 'admin_email_changed' ), 10, 3 );
98
+ }
99
+
100
+ /**
101
+ * Alert: Admin email changed.
102
+ *
103
+ * @param mixed $old_value - The old option value.
104
+ * @param mixed $new_value - The new option value.
105
+ * @param string $option - Option name.
106
+ * @since 3.0.0
107
+ */
108
+ public function admin_email_changed( $old_value, $new_value, $option ) {
109
+ // Check if the option is not empty and is admin_email.
110
+ if ( ! empty( $old_value ) && ! empty( $new_value )
111
+ && ! empty( $option ) && 'admin_email' === $option ) {
112
+ if ( $old_value != $new_value ) {
113
+ $this->plugin->alerts->Trigger(
114
+ 6003, array(
115
+ 'OldEmail' => $old_value,
116
+ 'NewEmail' => $new_value,
117
+ 'CurrentUserID' => wp_get_current_user()->ID,
118
+ )
119
+ );
120
+ }
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Method: Prune events function.
126
+ *
127
+ * @param int $count The number of deleted events.
128
+ * @param string $query Query that selected events for deletion.
129
+ */
130
+ public function EventPruneEvents( $count, $query ) {
131
+ $this->plugin->alerts->Trigger(
132
+ 6000, array(
133
+ 'EventCount' => $count,
134
+ 'PruneQuery' => $query,
135
+ )
136
+ );
137
+ }
138
+
139
+ /**
140
+ * 404 limit count.
141
+ *
142
+ * @return integer limit
143
+ */
144
+ protected function Get404LogLimit() {
145
+ return $this->plugin->settings->Get404LogLimit();
146
+ }
147
+
148
+ /**
149
+ * 404 visitor limit count.
150
+ *
151
+ * @return integer limit
152
+ */
153
+ protected function GetVisitor404LogLimit() {
154
+ return $this->plugin->settings->GetVisitor404LogLimit();
155
+ }
156
+
157
+ /**
158
+ * Expiration of the transient saved in the WP database.
159
+ *
160
+ * @return integer Time until expiration in seconds from now
161
+ */
162
+ protected function Get404Expiration() {
163
+ return 24 * 60 * 60;
164
+ }
165
+
166
+ /**
167
+ * Check 404 limit.
168
+ *
169
+ * @param integer $site_id - Blog ID.
170
+ * @param string $username - Username.
171
+ * @param string $ip - IP address.
172
+ * @return boolean passed limit true|false
173
+ */
174
+ protected function IsPast404Limit( $site_id, $username, $ip ) {
175
+ $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
176
+ $data = $get_fn( self::TRANSIENT_404 );
177
+ return ( false !== $data ) && isset( $data[ $site_id . ':' . $username . ':' . $ip ] ) && ($data[ $site_id . ':' . $username . ':' . $ip ] > $this->Get404LogLimit());
178
+ }
179
+
180
+ /**
181
+ * Check visitor 404 limit.
182
+ *
183
+ * @param integer $site_id - Blog ID.
184
+ * @param string $username - Username.
185
+ * @param string $ip - IP address.
186
+ * @return boolean passed limit true|false
187
+ */
188
+ protected function IsPastVisitor404Limit( $site_id, $username, $ip ) {
189
+ $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
190
+ $data = $get_fn( self::TRANSIENT_VISITOR_404 );
191
+ return ( false !== $data ) && isset( $data[ $site_id . ':' . $username . ':' . $ip ] ) && ( $data[ $site_id . ':' . $username . ':' . $ip ] > $this->GetVisitor404LogLimit() );
192
+ }
193
+
194
+ /**
195
+ * Increment 404 limit.
196
+ *
197
+ * @param integer $site_id - Blog ID.
198
+ * @param string $username - Username.
199
+ * @param string $ip - IP address.
200
+ */
201
+ protected function Increment404( $site_id, $username, $ip ) {
202
+ $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
203
+ $set_fn = $this->IsMultisite() ? 'set_site_transient' : 'set_transient';
204
+
205
+ $data = $get_fn( self::TRANSIENT_404 );
206
+ if ( ! $data ) {
207
+ $data = array();
208
+ }
209
+ if ( ! isset( $data[ $site_id . ':' . $username . ':' . $ip ] ) ) {
210
+ $data[ $site_id . ':' . $username . ':' . $ip ] = 1;
211
+ }
212
+ $data[ $site_id . ':' . $username . ':' . $ip ]++;
213
+ $set_fn( self::TRANSIENT_404, $data, $this->Get404Expiration() );
214
+ }
215
+
216
+ /**
217
+ * Increment visitor 404 limit.
218
+ *
219
+ * @param integer $site_id - Blog ID.
220
+ * @param string $username - Username.
221
+ * @param string $ip - IP address.
222
+ */
223
+ protected function IncrementVisitor404( $site_id, $username, $ip ) {
224
+ $get_fn = $this->IsMultisite() ? 'get_site_transient' : 'get_transient';
225
+ $set_fn = $this->IsMultisite() ? 'set_site_transient' : 'set_transient';
226
+
227
+ $data = $get_fn( self::TRANSIENT_VISITOR_404 );
228
+ if ( ! $data ) {
229
+ $data = array();
230
+ }
231
+ if ( ! isset( $data[ $site_id . ':' . $username . ':' . $ip ] ) ) {
232
+ $data[ $site_id . ':' . $username . ':' . $ip ] = 1;
233
+ }
234
+ $data[ $site_id . ':' . $username . ':' . $ip ]++;
235
+ $set_fn( self::TRANSIENT_VISITOR_404, $data, $this->Get404Expiration() );
236
+ }
237
+
238
+ /**
239
+ * Event 404 Not found.
240
+ */
241
+ public function Event404() {
242
+ $attempts = 1;
243
+
244
+ global $wp_query;
245
+ if ( ! $wp_query->is_404 ) {
246
+ return;
247
+ }
248
+ $msg = 'times';
249
+
250
+ list( $y, $m, $d ) = explode( '-', date( 'Y-m-d' ) );
251
+
252
+ $site_id = ( function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 0 );
253
+ $ip = $this->plugin->settings->GetMainClientIP();
254
+
255
+ if ( ! is_user_logged_in() ) {
256
+ $username = 'Website Visitor';
257
+ } else {
258
+ $username = wp_get_current_user()->user_login;
259
+ }
260
+
261
+ if ( 'Website Visitor' !== $username ) {
262
+ // Check if the alert is disabled from the "Enable/Disable Alerts" section.
263
+ if ( ! $this->plugin->alerts->IsEnabled( 6007 ) ) {
264
+ return;
265
+ }
266
+
267
+ if ( $this->IsPast404Limit( $site_id, $username, $ip ) ) {
268
+ return;
269
+ }
270
+
271
+ $obj_occurrence = new WSAL_Models_Occurrence();
272
+
273
+ $occ = $obj_occurrence->CheckAlert404(
274
+ array(
275
+ $ip,
276
+ $username,
277
+ 6007,
278
+ $site_id,
279
+ mktime( 0, 0, 0, $m, $d, $y ),
280
+ mktime( 0, 0, 0, $m, $d + 1, $y ) - 1,
281
+ )
282
+ );
283
+
284
+ $occ = count( $occ ) ? $occ[0] : null;
285
+ if ( ! empty( $occ ) ) {
286
+ // update existing record.
287
+ $this->Increment404( $site_id, $username, $ip );
288
+ $new = ( (int) $occ->GetMetaValue( 'Attempts', 0 ) ) + 1;
289
+
290
+ if ( $new > $this->Get404LogLimit() ) {
291
+ $new = 'more than ' . $this->Get404LogLimit();
292
+ $msg .= ' This could possible be a scan, therefore keep an eye on the activity from this IP Address';
293
+ }
294
+
295
+ $link_file = $this->WriteLog( $new, $ip, $username );
296
+
297
+ $occ->UpdateMetaValue( 'Attempts', $new );
298
+ $occ->UpdateMetaValue( 'Username', $username );
299
+ $occ->UpdateMetaValue( 'Msg', $msg );
300
+ if ( ! empty( $link_file ) ) {
301
+ $occ->UpdateMetaValue( 'LinkFile', $link_file );
302
+ }
303
+ $occ->created_on = null;
304
+ $occ->Save();
305
+ } else {
306
+ $link_file = $this->WriteLog( 1, $ip, $username );
307
+ // Create a new record.
308
+ $fields = array(
309
+ 'Attempts' => 1,
310
+ 'Username' => $username,
311
+ 'Msg' => $msg,
312
+ );
313
+ if ( ! empty( $link_file ) ) {
314
+ $fields['LinkFile'] = $link_file;
315
+ }
316
+ $this->plugin->alerts->Trigger( 6007, $fields );
317
+ }
318
+ } else {
319
+ // Check if the alert is disabled from the "Enable/Disable Alerts" section.
320
+ if ( ! $this->plugin->alerts->IsEnabled( 6023 ) ) {
321
+ return;
322
+ }
323
+
324
+ if ( $this->IsPastVisitor404Limit( $site_id, $username, $ip ) ) {
325
+ return;
326
+ }
327
+
328
+ $obj_occurrence = new WSAL_Models_Occurrence();
329
+
330
+ $occ = $obj_occurrence->CheckAlert404(
331
+ array(
332
+ $ip,
333
+ $username,
334
+ 6023,
335
+ $site_id,
336
+ mktime( 0, 0, 0, $m, $d, $y ),
337
+ mktime( 0, 0, 0, $m, $d + 1, $y ) - 1,
338
+ )
339
+ );
340
+
341
+ $occ = count( $occ ) ? $occ[0] : null;
342
+ if ( ! empty( $occ ) ) {
343
+ // Update existing record.
344
+ $this->IncrementVisitor404( $site_id, $username, $ip );
345
+ $new = ( (int) $occ->GetMetaValue( 'Attempts', 0 ) ) + 1;
346
+
347
+ if ( $new > $this->GetVisitor404LogLimit() ) {
348
+ $new = 'more than ' . $this->GetVisitor404LogLimit();
349
+ $msg .= ' This could possible be a scan, therefore keep an eye on the activity from this IP Address';
350
+ }
351
+
352
+ $link_file = $this->WriteLog( $new, $ip, $username, false );
353
+
354
+ $occ->UpdateMetaValue( 'Attempts', $new );
355
+ $occ->UpdateMetaValue( 'Username', $username );
356
+ $occ->UpdateMetaValue( 'Msg', $msg );
357
+ if ( ! empty( $link_file ) ) {
358
+ $occ->UpdateMetaValue( 'LinkFile', $link_file );
359
+ }
360
+ $occ->created_on = null;
361
+ $occ->Save();
362
+ } else {
363
+ $link_file = $this->WriteLog( 1, $ip, $username, false );
364
+ // Create a new record.
365
+ $fields = array(
366
+ 'Attempts' => 1,
367
+ 'Username' => $username,
368
+ 'Msg' => $msg,
369
+ );
370
+ if ( ! empty( $link_file ) ) {
371
+ $fields['LinkFile'] = $link_file;
372
+ }
373
+ $this->plugin->alerts->Trigger( 6023, $fields );
374
+ }
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Triggered when a user accesses the admin area.
380
+ */
381
+ public function EventAdminInit() {
382
+ // Filter global arrays for security.
383
+ $post_array = filter_input_array( INPUT_POST );
384
+ $get_array = filter_input_array( INPUT_GET );
385
+ $server_array = filter_input_array( INPUT_SERVER );
386
+
387
+ // Destroy all the session of the same user from user profile page.
388
+ if ( isset( $post_array['action'] ) && ( 'destroy-sessions' == $post_array['action'] ) && isset( $post_array['user_id'] ) ) {
389
+ $this->plugin->alerts->Trigger(
390
+ 1006, array(
391
+ 'TargetUserID' => $post_array['user_id'],
392
+ )
393
+ );
394
+ }
395
+
396
+ // Make sure user can actually modify target options.
397
+ if ( ! current_user_can( 'manage_options' ) && isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'update' ) ) {
398
+ return;
399
+ }
400
+
401
+ $actype = basename( $server_array['SCRIPT_NAME'], '.php' );
402
+ $is_option_page = 'options' === $actype;
403
+ $is_network_settings = 'settings' === $actype;
404
+ $is_permalink_page = 'options-permalink' === $actype;
405
+
406
+ // WordPress URL changed.
407
+ if ( $is_option_page
408
+ && wp_verify_nonce( $post_array['_wpnonce'], 'general-options' )
409
+ && ! empty( $post_array['siteurl'] ) ) {
410
+ $old_siteurl = get_option( 'siteurl' );
411
+ $new_siteurl = isset( $post_array['siteurl'] ) ? $post_array['siteurl'] : '';
412
+ if ( $old_siteurl !== $new_siteurl ) {
413
+ $this->plugin->alerts->Trigger(
414
+ 6024, array(
415
+ 'old_url' => $old_siteurl,
416
+ 'new_url' => $new_siteurl,
417
+ 'CurrentUserID' => wp_get_current_user()->ID,
418
+ )
419
+ );
420
+ }
421
+ }
422
+
423
+ // Site URL changed.
424
+ if ( $is_option_page
425
+ && wp_verify_nonce( $post_array['_wpnonce'], 'general-options' )
426
+ && ! empty( $post_array['home'] ) ) {
427
+ $old_url = get_option( 'home' );
428
+ $new_url = isset( $post_array['home'] ) ? $post_array['home'] : '';
429
+ if ( $old_url !== $new_url ) {
430
+ $this->plugin->alerts->Trigger(
431
+ 6025, array(
432
+ 'old_url' => $old_url,
433
+ 'new_url' => $new_url,
434
+ 'CurrentUserID' => wp_get_current_user()->ID,
435
+ )
436
+ );
437
+ }
438
+ }
439
+
440
+ // Registeration Option.
441
+ if ( $is_option_page
442
+ && wp_verify_nonce( $post_array['_wpnonce'], 'general-options' )
443
+ && ( get_option( 'users_can_register' ) xor isset( $post_array['users_can_register'] ) ) ) {
444
+ $old = get_option( 'users_can_register' ) ? 'Enabled' : 'Disabled';
445
+ $new = isset( $post_array['users_can_register'] ) ? 'Enabled' : 'Disabled';
446
+ if ( $old != $new ) {
447
+ $this->plugin->alerts->Trigger(
448
+ 6001, array(
449
+ 'OldValue' => $old,
450
+ 'NewValue' => $new,
451
+ 'CurrentUserID' => wp_get_current_user()->ID,
452
+ )
453
+ );
454
+ }
455
+ }
456
+
457
+ // Default Role option.
458
+ if ( $is_option_page && wp_verify_nonce( $post_array['_wpnonce'], 'general-options' ) && ! empty( $post_array['default_role'] ) ) {
459
+ $old = get_option( 'default_role' );
460
+ $new = trim( $post_array['default_role'] );
461
+ if ( $old != $new ) {
462
+ $this->plugin->alerts->Trigger(
463
+ 6002, array(
464
+ 'OldRole' => $old,
465
+ 'NewRole' => $new,
466
+ 'CurrentUserID' => wp_get_current_user()->ID,
467
+ )
468
+ );
469
+ }
470
+ }
471
+
472
+ // Admin Email Option.
473
+ if ( $is_option_page && wp_verify_nonce( $post_array['_wpnonce'], 'general-options' ) && ! empty( $post_array['admin_email'] ) ) {
474
+ $old = get_option( 'admin_email' );
475
+ $new = trim( $post_array['admin_email'] );
476
+ if ( $old != $new ) {
477
+ $this->plugin->alerts->Trigger(
478
+ 6003, array(
479
+ 'OldEmail' => $old,
480
+ 'NewEmail' => $new,
481
+ 'CurrentUserID' => wp_get_current_user()->ID,
482
+ )
483
+ );
484
+ }
485
+ }
486
+
487
+ // Admin Email of Network.
488
+ if ( $is_network_settings && ! empty( $post_array['admin_email'] ) ) {
489
+ $old = get_site_option( 'admin_email' );
490
+ $new = trim( $post_array['admin_email'] );
491
+ if ( $old != $new ) {
492
+ $this->plugin->alerts->Trigger(
493
+ 6003, array(
494
+ 'OldEmail' => $old,
495
+ 'NewEmail' => $new,
496
+ 'CurrentUserID' => wp_get_current_user()->ID,
497
+ )
498
+ );
499
+ }
500
+ }
501
+
502
+ // Permalinks changed.
503
+ if ( $is_permalink_page && ! empty( $post_array['permalink_structure'] ) ) {
504
+ $old = get_option( 'permalink_structure' );
505
+ $new = trim( $post_array['permalink_structure'] );
506
+ if ( $old != $new ) {
507
+ $this->plugin->alerts->Trigger(
508
+ 6005, array(
509
+ 'OldPattern' => $old,
510
+ 'NewPattern' => $new,
511
+ 'CurrentUserID' => wp_get_current_user()->ID,
512
+ )
513
+ );
514
+ }
515
+ }
516
+
517
+ // Core Update.
518
+ if ( isset( $get_array['action'] ) && 'do-core-upgrade' === $get_array['action'] && isset( $post_array['version'] ) ) {
519
+ $old_version = get_bloginfo( 'version' );
520
+ $new_version = $post_array['version'];
521
+ if ( $old_version != $new_version ) {
522
+ $this->plugin->alerts->Trigger(
523
+ 6004, array(
524
+ 'OldVersion' => $old_version,
525
+ 'NewVersion' => $new_version,
526
+ )
527
+ );
528
+ }
529
+ }
530
+
531
+ /* BBPress Forum support Setting */
532
+ if ( isset( $post_array['action'] ) && 'update' === $post_array['action'] && isset( $post_array['_bbp_default_role'] ) ) {
533
+ $old_role = get_option( '_bbp_default_role' );
534
+ $new_role = $post_array['_bbp_default_role'];
535
+ if ( $old_role != $new_role ) {
536
+ $this->plugin->alerts->Trigger(
537
+ 8009, array(
538
+ 'OldRole' => $old_role,
539
+ 'NewRole' => $new_role,
540
+ )
541
+ );
542
+ }
543
+ }
544
+
545
+ if ( isset( $post_array['action'] ) && 'update' === $post_array['action'] && isset( $post_array['option_page'] ) && ( 'bbpress' === $post_array['option_page'] ) ) {
546
+ // Anonymous posting.
547
+ $allow_anonymous = get_option( '_bbp_allow_anonymous' );
548
+ $old_status = ! empty( $allow_anonymous ) ? 1 : 0;
549
+ $new_status = ! empty( $post_array['_bbp_allow_anonymous'] ) ? 1 : 0;
550
+ if ( $old_status !== $new_status ) {
551
+ $status = ( 1 === $new_status ) ? 'Enabled' : 'Disabled';
552
+ $this->plugin->alerts->Trigger(
553
+ 8010, array(
554
+ 'Status' => $status,
555
+ )
556
+ );
557
+ }
558
+
559
+ // Disallow editing after.
560
+ $bbp_edit_lock = get_option( '_bbp_edit_lock' );
561
+ $old_time = ! empty( $bbp_edit_lock ) ? $bbp_edit_lock : '';
562
+ $new_time = ! empty( $post_array['_bbp_edit_lock'] ) ? $post_array['_bbp_edit_lock'] : '';
563
+ if ( $old_time != $new_time ) {
564
+ $this->plugin->alerts->Trigger(
565
+ 8012, array(
566
+ 'OldTime' => $old_time,
567
+ 'NewTime' => $new_time,
568
+ )
569
+ );
570
+ }
571
+
572
+ // Throttle posting every.
573
+ $bbp_throttle_time = get_option( '_bbp_throttle_time' );
574
+ $old_time2 = ! empty( $bbp_throttle_time ) ? $bbp_throttle_time : '';
575
+ $new_time2 = ! empty( $post_array['_bbp_throttle_time'] ) ? $post_array['_bbp_throttle_time'] : '';
576
+ if ( $old_time2 != $new_time2 ) {
577
+ $this->plugin->alerts->Trigger(
578
+ 8013, array(
579
+ 'OldTime' => $old_time2,
580
+ 'NewTime' => $new_time2,
581
+ )
582
+ );
583
+ }
584
+ }
585
+ }
586
+
587
+ /**
588
+ * WordPress auto core update.
589
+ *
590
+ * @param array $automatic - Automatic update array.
591
+ */
592
+ public function WPUpdate( $automatic ) {
593
+ if ( isset( $automatic['core'][0] ) ) {
594
+ $obj = $automatic['core'][0];
595
+ $old_version = get_bloginfo( 'version' );
596
+ $this->plugin->alerts->Trigger(
597
+ 6004, array(
598
+ 'OldVersion' => $old_version,
599
+ 'NewVersion' => $obj->item->version . ' (auto update)',
600
+ )
601
+ );
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Purge log files older than one month.
607
+ */
608
+ public function LogFilesPruning() {
609
+ if ( $this->plugin->GetGlobalOption( 'purge-404-log', 'off' ) == 'on' ) {
610
+ $upload_dir = wp_upload_dir();
611
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/users/';
612
+ if ( is_dir( $uploads_dir_path ) ) {
613
+ if ( $handle = opendir( $uploads_dir_path ) ) {
614
+ while ( false !== ($entry = readdir( $handle )) ) {
615
+ if ( '.' != $entry && '..' != $entry ) {
616
+ if ( file_exists( $uploads_dir_path . $entry ) ) {
617
+ $modified = filemtime( $uploads_dir_path . $entry );
618
+ if ( $modified < strtotime( '-4 weeks' ) ) {
619
+ // Delete file.
620
+ unlink( $uploads_dir_path . $entry );
621
+ }
622
+ }
623
+ }
624
+ }
625
+ closedir( $handle );
626
+ }
627
+ }
628
+ }
629
+ if ( 'on' == $this->plugin->GetGlobalOption( 'purge-visitor-404-log', 'off' ) ) {
630
+ $upload_dir = wp_upload_dir();
631
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/visitors/';
632
+ if ( is_dir( $uploads_dir_path ) ) {
633
+ if ( $handle = opendir( $uploads_dir_path ) ) {
634
+ while ( false !== ( $entry = readdir( $handle ) ) ) {
635
+ if ( $entry != '.' && $entry != '..' ) {
636
+ if ( file_exists( $uploads_dir_path . $entry ) ) {
637
+ $modified = filemtime( $uploads_dir_path . $entry );
638
+ if ( $modified < strtotime( '-4 weeks' ) ) {
639
+ // Delete file.
640
+ unlink( $uploads_dir_path . $entry );
641
+ }
642
+ }
643
+ }
644
+ }
645
+ closedir( $handle );
646
+ }
647
+ }
648
+ }
649
+ }
650
+
651
+ /**
652
+ * Events from 6008 to 6018.
653
+ *
654
+ * @param array $whitelist - White list options.
655
+ */
656
+ public function EventOptions( $whitelist = null ) {
657
+ // Filter global arrays for security.
658
+ $post_array = filter_input_array( INPUT_POST );
659
+ $get_array = filter_input_array( INPUT_GET );
660
+
661
+ if ( isset( $post_array['option_page'] ) && 'reading' === $post_array['option_page'] ) {
662
+ $old_status = (int) get_option( 'blog_public', 1 );
663
+ $new_status = isset( $post_array['blog_public'] ) ? 0 : 1;
664
+ if ( $old_status !== $new_status ) {
665
+ $this->plugin->alerts->Trigger(
666
+ 6008, array(
667
+ 'Status' => ( 0 === $new_status ) ? 'Enabled' : 'Disabled',
668
+ )
669
+ );
670
+ }
671
+ }
672
+
673
+ if ( isset( $post_array['option_page'] ) && 'discussion' === $post_array['option_page'] ) {
674
+ $old_status = get_option( 'default_comment_status', 'closed' );
675
+ $new_status = isset( $post_array['default_comment_status'] ) ? 'open' : 'closed';
676
+ if ( $old_status !== $new_status ) {
677
+ $this->plugin->alerts->Trigger(
678
+ 6009, array(
679
+ 'Status' => ( 'open' === $new_status ) ? 'Enabled' : 'Disabled',
680
+ )
681
+ );
682
+ }
683
+
684
+ $old_status = (int) get_option( 'require_name_email', 0 );
685
+ $new_status = isset( $post_array['require_name_email'] ) ? 1 : 0;
686
+ if ( $old_status !== $new_status ) {
687
+ $this->plugin->alerts->Trigger(
688
+ 6010, array(
689
+ 'Status' => ( 1 === $new_status ) ? 'Enabled' : 'Disabled',
690
+ )
691
+ );
692
+ }
693
+
694
+ $old_status = (int) get_option( 'comment_registration', 0 );
695
+ $new_status = isset( $post_array['comment_registration'] ) ? 1 : 0;
696
+ if ( $old_status !== $new_status ) {
697
+ $this->plugin->alerts->Trigger(
698
+ 6011, array(
699
+ 'Status' => ( 1 === $new_status ) ? 'Enabled' : 'Disabled',
700
+ )
701
+ );
702
+ }
703
+
704
+ $old_status = (int) get_option( 'close_comments_for_old_posts', 0 );
705
+ $new_status = isset( $post_array['close_comments_for_old_posts'] ) ? 1 : 0;
706
+ if ( $old_status !== $new_status ) {
707
+ $value = isset( $post_array['close_comments_days_old'] ) ? $post_array['close_comments_days_old'] : 0;
708
+ $this->plugin->alerts->Trigger(
709
+ 6012, array(
710
+ 'Status' => ( 1 === $new_status ) ? 'Enabled' : 'Disabled',
711
+ 'Value' => $value,
712
+ )
713
+ );
714
+ }
715
+
716
+ $old_value = get_option( 'close_comments_days_old', 0 );
717
+ $new_value = isset( $post_array['close_comments_days_old'] ) ? $post_array['close_comments_days_old'] : 0;
718
+ if ( $old_value !== $new_value ) {
719
+ $this->plugin->alerts->Trigger(
720
+ 6013, array(
721
+ 'OldValue' => $old_value,
722
+ 'NewValue' => $new_value,
723
+ )
724
+ );
725
+ }
726
+
727
+ $old_status = (int) get_option( 'comment_moderation', 0 );
728
+ $new_status = isset( $post_array['comment_moderation'] ) ? 1 : 0;
729
+ if ( $old_status !== $new_status ) {
730
+ $this->plugin->alerts->Trigger(
731
+ 6014, array(
732
+ 'Status' => ( 1 === $new_status ) ? 'Enabled' : 'Disabled',
733
+ )
734
+ );
735
+ }
736
+
737
+ $old_status = (int) get_option( 'comment_whitelist', 0 );
738
+ $new_status = isset( $post_array['comment_whitelist'] ) ? 1 : 0;
739
+ if ( $old_status !== $new_status ) {
740
+ $this->plugin->alerts->Trigger(
741
+ 6015, array(
742
+ 'Status' => ( 1 === $new_status ) ? 'Enabled' : 'Disabled',
743
+ )
744
+ );
745
+ }
746
+
747
+ $old_value = get_option( 'comment_max_links', 0 );
748
+ $new_value = isset( $post_array['comment_max_links'] ) ? $post_array['comment_max_links'] : 0;
749
+ if ( $old_value !== $new_value ) {
750
+ $this->plugin->alerts->Trigger(
751
+ 6016, array(
752
+ 'OldValue' => $old_value,
753
+ 'NewValue' => $new_value,
754
+ )
755
+ );
756
+ }
757
+
758
+ $old_value = get_option( 'moderation_keys', 0 );
759
+ $new_value = isset( $post_array['moderation_keys'] ) ? $post_array['moderation_keys'] : 0;
760
+ if ( $old_value !== $new_value ) {
761
+ $this->plugin->alerts->Trigger( 6017, array() );
762
+ }
763
+
764
+ $old_value = get_option( 'blacklist_keys', 0 );
765
+ $new_value = isset( $post_array['blacklist_keys'] ) ? $post_array['blacklist_keys'] : 0;
766
+ if ( $old_value !== $new_value ) {
767
+ $this->plugin->alerts->Trigger( 6018, array() );
768
+ }
769
+ }
770
+ return $whitelist;
771
+ }
772
+
773
+ /**
774
+ * Write Log.
775
+ *
776
+ * Write a new line on 404 log file.
777
+ * Folder: /uploads/wp-security-audit-log/404s/
778
+ *
779
+ * @param int $attempts - Number of attempt.
780
+ * @param string $ip - IP address.
781
+ * @param string $username - Username.
782
+ * @param bool $logged_in - True if logged in.
783
+ */
784
+ private function WriteLog( $attempts, $ip, $username = '', $logged_in = true ) {
785
+ $name_file = null;
786
+
787
+ if ( $logged_in ) {
788
+ if ( 'on' === $this->plugin->GetGlobalOption( 'log-404', 'off' ) ) {
789
+ // Filter global arrays for security.
790
+ $server_array = filter_input_array( INPUT_SERVER );
791
+
792
+ // Request URL.
793
+ $request_uri = filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL );
794
+ $url = site_url() . $request_uri;
795
+
796
+ // Get option to log referrer.
797
+ $log_referrer = $this->plugin->GetGlobalOption( 'log-404-referrer' );
798
+
799
+ if ( 'on' === $log_referrer ) {
800
+ // Get the referer.
801
+ if ( isset( $server_array['HTTP_REFERER'] ) ) {
802
+ $referrer = filter_input( INPUT_SERVER, 'HTTP_REFERER', FILTER_SANITIZE_URL );
803
+ }
804
+
805
+ // Create/Append to the log file.
806
+ $data = 'Request URL ' . $url . ' Referer ' . $referrer . ',';
807
+ } else {
808
+ // Create/Append to the log file.
809
+ $data = 'Request URL ' . $url . ',';
810
+ }
811
+
812
+ if ( ! is_user_logged_in() ) {
813
+ $username = '';
814
+ } else {
815
+ $username = $username . '_';
816
+ }
817
+
818
+ if ( '127.0.0.1' == $ip || '::1' == $ip ) {
819
+ $ip = 'localhost';
820
+ }
821
+ $upload_dir = wp_upload_dir();
822
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/users/';
823
+ $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/users/';
824
+
825
+ // Check directory.
826
+ if ( $this->CheckDirectory( $uploads_dir_path ) ) {
827
+ $filename = '6007_' . date( 'Ymd' ) . '.log';
828
+ $fp = $uploads_dir_path . $filename;
829
+ $name_file = $uploads_url . $filename;
830
+ if ( ! $file = fopen( $fp, 'a' ) ) {
831
+ $i = 1;
832
+ $file_opened = false;
833
+ do {
834
+ $fp2 = substr( $fp, 0, -4 ) . '_' . $i . '.log';
835
+ if ( ! file_exists( $fp2 ) ) {
836
+ if ( $file = fopen( $fp2, 'a' ) ) {
837
+ $file_opened = true;
838
+ $name_file = $uploads_url . substr( $name_file, 0, -4 ) . '_' . $i . '.log';
839
+ }
840
+ } else {
841
+ $latest_filename = $this->GetLastModified( $uploads_dir_path, $filename );
842
+ $fp_last = $uploads_dir_path . $latest_filename;
843
+ if ( $file = fopen( $fp_last, 'a' ) ) {
844
+ $file_opened = true;
845
+ $name_file = $uploads_url . $latest_filename;
846
+ }
847
+ }
848
+ $i++;
849
+ } while ( ! $file_opened );
850
+ }
851
+ fwrite( $file, sprintf( "%s\n", $data ) );
852
+ fclose( $file );
853
+ }
854
+ }
855
+ } else {
856
+ if ( 'on' === $this->plugin->GetGlobalOption( 'log-visitor-404', 'off' ) ) {
857
+ // Filter global arrays for security.
858
+ $server_array = filter_input_array( INPUT_SERVER );
859
+
860
+ // Request URL.
861
+ $request_uri = filter_input( INPUT_SERVER, 'REQUEST_URI', FILTER_SANITIZE_URL );
862
+ $url = site_url() . $request_uri;
863
+
864
+ // Get option to log referrer.
865
+ $log_referrer = $this->plugin->GetGlobalOption( 'log-visitor-404-referrer' );
866
+
867
+ if ( 'on' === $log_referrer ) {
868
+ // Get the referer.
869
+ if ( isset( $server_array['HTTP_REFERER'] ) ) {
870
+ $referrer = filter_input( INPUT_SERVER, 'HTTP_REFERER', FILTER_SANITIZE_URL );
871
+ }
872
+
873
+ // Create/Append to the log file.
874
+ $data = 'Request URL ' . $url . ' Referer ' . $referrer . ',';
875
+ } else {
876
+ // Create/Append to the log file.
877
+ $data = 'Request URL ' . $url . ',';
878
+ }
879
+
880
+ $username = '';
881
+
882
+ if ( '127.0.0.1' == $ip || '::1' == $ip ) {
883
+ $ip = 'localhost';
884
+ }
885
+ $upload_dir = wp_upload_dir();
886
+ $uploads_dir_path = trailingslashit( $upload_dir['basedir'] ) . 'wp-security-audit-log/404s/visitors/';
887
+ $uploads_url = trailingslashit( $upload_dir['baseurl'] ) . 'wp-security-audit-log/404s/visitors/';
888
+
889
+ // Check directory.
890
+ if ( $this->CheckDirectory( $uploads_dir_path ) ) {
891
+ $filename = '6023_' . date( 'Ymd' ) . '.log';
892
+ $fp = $uploads_dir_path . $filename;
893
+ $name_file = $uploads_url . $filename;
894
+ if ( ! $file = fopen( $fp, 'a' ) ) {
895
+ $i = 1;
896
+ $file_opened = false;
897
+ do {
898
+ $fp2 = substr( $fp, 0, -4 ) . '_' . $i . '.log';
899
+ if ( ! file_exists( $fp2 ) ) {
900
+ if ( $file = fopen( $fp2, 'a' ) ) {
901
+ $file_opened = true;
902
+ $name_file = $uploads_url . substr( $name_file, 0, -4 ) . '_' . $i . '.log';
903
+ }
904
+ } else {
905
+ $latest_filename = $this->GetLastModified( $uploads_dir_path, $filename );
906
+ $fp_last = $uploads_dir_path . $latest_filename;
907
+ if ( $file = fopen( $fp_last, 'a' ) ) {
908
+ $file_opened = true;
909
+ $name_file = $uploads_url . $latest_filename;
910
+ }
911
+ }
912
+ $i++;
913
+ } while ( ! $file_opened );
914
+ }
915
+ fwrite( $file, sprintf( "%s\n", $data ) );
916
+ fclose( $file );
917
+ }
918
+ }
919
+ }
920
+
921
+ return $name_file;
922
+ }
923
+
924
+ /**
925
+ * Get the latest file modified.
926
+ *
927
+ * @param string $uploads_dir_path - Uploads directory path.
928
+ * @param string $filename - File name.
929
+ * @return string $latest_filename - File name.
930
+ */
931
+ private function GetLastModified( $uploads_dir_path, $filename ) {
932
+ $filename = substr( $filename, 0, -4 );
933
+ $latest_mtime = 0;
934
+ $latest_filename = '';
935
+ if ( $handle = opendir( $uploads_dir_path ) ) {
936
+ while ( false !== ($entry = readdir( $handle )) ) {
937
+ if ( '.' != $entry && '..' != $entry ) {
938
+ $entry = strip_tags( $entry ); // Strip HTML Tags.
939
+ $entry = preg_replace( '/[\r\n\t ]+/', ' ', $entry ); // Remove Break/Tabs/Return Carriage.
940
+ $entry = preg_replace( '/[\"\*\/\:\<\>\?\'\|]+/', ' ', $entry ); // Remove Illegal Chars for folder and filename.
941
+ if ( preg_match( '/^' . $filename . '/i', $entry ) > 0 ) {
942
+ if ( filemtime( $uploads_dir_path . $entry ) > $latest_mtime ) {
943
+ $latest_mtime = filemtime( $uploads_dir_path . $entry );
944
+ $latest_filename = $entry;
945
+ }
946
+ }
947
+ }
948
+ }
949
+ closedir( $handle );
950
+ }
951
+ return $latest_filename;
952
+ }
953
  }
classes/Sensors/UserProfile.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * User Profiles sensor.
6
  *
7
  * 4000 New user was created on WordPress
@@ -16,188 +28,258 @@
16
  * 4009 User revoked from Super Admin privileges
17
  * 4013 The forum role of a user was changed by another WordPress user
18
  * 4014 User opened the profile page of another user
 
 
 
19
  */
20
- class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor
21
- {
22
- /**
23
- * Listening to events using WP hooks.
24
- */
25
- public function HookEvents()
26
- {
27
- add_action('admin_init', array($this, 'EventAdminInit'));
28
- add_action('user_register', array($this, 'EventUserRegister'));
29
- add_action('edit_user_profile_update', array($this, 'EventUserChanged'));
30
- add_action('personal_options_update', array($this, 'EventUserChanged'));
31
- add_action('delete_user', array($this, 'EventUserDeleted'));
32
- add_action('wpmu_delete_user', array($this, 'EventUserDeleted'));
33
- add_action('set_user_role', array($this, 'EventUserRoleChanged'), 10, 3);
34
-
35
- add_action('edit_user_profile', array($this, 'EventOpenProfile'), 10, 1);
36
- }
37
-
38
- protected $old_superadmins;
39
-
40
- /**
41
- * Triggered when a user accesses the admin area.
42
- */
43
- public function EventAdminInit()
44
- {
45
- if ($this->IsMultisite()) {
46
- $this->old_superadmins = get_super_admins();
47
- }
48
- }
49
-
50
- public function EventUserRegister($user_id)
51
- {
52
- $user = get_userdata($user_id);
53
- $ismu = function_exists('is_multisite') && is_multisite();
54
- $event = $ismu ? 4012 : (is_user_logged_in() ? 4001 : 4000);
55
- $current_user = wp_get_current_user();
56
- $this->plugin->alerts->Trigger($event, array(
57
- 'NewUserID' => $user_id,
58
- 'UserChanger' => !empty($current_user) ? $current_user->user_login : '',
59
- 'NewUserData' => (object)array(
60
- 'Username' => $user->user_login,
61
- 'FirstName' => $user->user_firstname,
62
- 'LastName' => $user->user_lastname,
63
- 'Email' => $user->user_email,
64
- 'Roles' => is_array($user->roles) ? implode(', ', $user->roles) : $user->roles,
65
- ),
66
- ), true);
67
- }
68
-
69
- public function EventUserRoleChanged($user_id, $role, $oldRoles)
70
- {
71
- $user = get_userdata($user_id);
72
- $aBbpRoles = array('bbp_spectator', 'bbp_moderator', 'bbp_participant', 'bbp_keymaster', 'bbp_blocked');
73
- // remove any BBPress roles
74
- if (is_array($oldRoles)) {
75
- foreach ($oldRoles as $value) {
76
- if (in_array($value, $aBbpRoles)) {
77
- if ( isset( $_POST['bbp-forums-role'] ) && $_POST['bbp-forums-role'] != $value ) {
78
- $current_user = wp_get_current_user();
79
- $this->plugin->alerts->TriggerIf(4013, array(
80
- 'TargetUsername' => $user->user_login,
81
- 'OldRole' => ucfirst(substr($value, 4)),
82
- 'NewRole' => ( isset( $_POST['bbp-forums-role'] ) ) ? ucfirst(substr($_POST['bbp-forums-role'], 4)) : false,
83
- 'UserChanger' => $current_user->user_login
84
- ));
85
- }
86
- }
87
- }
88
- $oldRoles = array_diff($oldRoles, $aBbpRoles);
89
- }
90
- $oldRole = count($oldRoles) ? implode(', ', $oldRoles) : '';
91
- $newRole = $role;
92
- if ($oldRole != $newRole) {
93
- $this->plugin->alerts->TriggerIf(4002, array(
94
- 'TargetUserID' => $user_id,
95
- 'TargetUsername' => $user->user_login,
96
- 'OldRole' => $oldRole,
97
- 'NewRole' => $newRole,
98
- ), array($this, 'MustNotContainUserChanges'));
99
- }
100
- }
101
-
102
- public function EventUserChanged($user_id)
103
- {
104
- $user = get_userdata($user_id);
105
-
106
- // password changed
107
- if (!empty($_REQUEST['pass1']) && !empty($_REQUEST['pass2'])) {
108
- if (trim($_REQUEST['pass1']) == trim($_REQUEST['pass2'])) {
109
- $event = $user_id == get_current_user_id() ? 4003 : 4004;
110
- $this->plugin->alerts->Trigger($event, array(
111
- 'TargetUserID' => $user_id,
112
- 'TargetUserData' => (object)array(
113
- 'Username' => $user->user_login,
114
- 'Roles' => is_array($user->roles) ? implode(', ', $user->roles) : $user->roles,
115
- ),
116
- ));
117
- }
118
- }
119
-
120
- // email changed
121
- if (!empty($_REQUEST['email'])) {
122
- $oldEmail = $user->user_email;
123
- $newEmail = trim($_REQUEST['email']);
124
- if ($oldEmail != $newEmail) {
125
- $event = $user_id == get_current_user_id() ? 4005 : 4006;
126
- $this->plugin->alerts->Trigger($event, array(
127
- 'TargetUserID' => $user_id,
128
- 'TargetUsername' => $user->user_login,
129
- 'OldEmail' => $oldEmail,
130
- 'NewEmail' => $newEmail,
131
- ));
132
- }
133
- }
134
-
135
- if ($this->IsMultisite()) {
136
- $username = $user->user_login;
137
- $enabled = isset($_REQUEST['super_admin']);
138
-
139
- if ($user_id != get_current_user_id()) {
140
- // super admin enabled
141
- if ($enabled && !in_array($username, $this->old_superadmins)) {
142
- $this->plugin->alerts->Trigger(4008, array(
143
- 'TargetUserID' => $user_id,
144
- 'TargetUsername' => $user->user_login,
145
- ));
146
- }
147
-
148
- // super admin disabled
149
- if (!$enabled && in_array($username, $this->old_superadmins)) {
150
- $this->plugin->alerts->Trigger(4009, array(
151
- 'TargetUserID' => $user_id,
152
- 'TargetUsername' => $user->user_login,
153
- ));
154
- }
155
-
156
- }
157
- }
158
- }
159
-
160
- public function EventUserDeleted($user_id)
161
- {
162
- $user = get_userdata($user_id);
163
- $role = is_array($user->roles) ? implode(', ', $user->roles) : $user->roles;
164
- $this->plugin->alerts->TriggerIf(4007, array(
165
- 'TargetUserID' => $user_id,
166
- 'TargetUserData' => (object)array(
167
- 'Username' => $user->user_login,
168
- 'FirstName' => $user->user_firstname,
169
- 'LastName' => $user->user_lastname,
170
- 'Email' => $user->user_email,
171
- 'Roles' => $role ? $role : 'none',
172
- ),
173
- ), array($this, 'MustNotContainCreateUser'));
174
- }
175
-
176
- public function EventOpenProfile( $user ) {
177
- if ( ! empty( $user ) ) {
178
- $current_user = wp_get_current_user();
179
- $updated = ( isset( $_GET['updated'] ) ) ? true : false;
180
- if ( ! empty( $current_user ) && ( $user->ID !== $current_user->ID ) && empty( $updated ) ) {
181
- $this->plugin->alerts->Trigger( 4014, array(
182
- 'UserChanger' => $current_user->user_login,
183
- 'TargetUsername' => $user->user_login,
184
- ) );
185
- }
186
- }
187
- }
188
-
189
- public function MustNotContainCreateUser(WSAL_AlertManager $mgr)
190
- {
191
- return !$mgr->WillTrigger(4012);
192
- }
193
-
194
- public function MustNotContainUserChanges(WSAL_AlertManager $mgr)
195
- {
196
- return !( $mgr->WillOrHasTriggered(4010)
197
- || $mgr->WillOrHasTriggered(4011)
198
- || $mgr->WillOrHasTriggered(4012)
199
- || $mgr->WillOrHasTriggered(4000)
200
- || $mgr->WillOrHasTriggered(4001)
201
- );
202
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  }
1
  <?php
2
  /**
3
+ * Sensor: User Profile
4
+ *
5
+ * User profile sensor file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * User Profiles sensor.
18
  *
19
  * 4000 New user was created on WordPress
28
  * 4009 User revoked from Super Admin privileges
29
  * 4013 The forum role of a user was changed by another WordPress user
30
  * 4014 User opened the profile page of another user
31
+ *
32
+ * @package Wsal
33
+ * @subpackage Sensors
34
  */
35
+ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
36
+
37
+ /**
38
+ * Listening to events using WP hooks.
39
+ */
40
+ public function HookEvents() {
41
+ add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
42
+ add_action( 'user_register', array( $this, 'EventUserRegister' ) );
43
+ add_action( 'edit_user_profile_update', array( $this, 'EventUserChanged' ) );
44
+ add_action( 'personal_options_update', array( $this, 'EventUserChanged' ) );
45
+ add_action( 'delete_user', array( $this, 'EventUserDeleted' ) );
46
+ add_action( 'wpmu_delete_user', array( $this, 'EventUserDeleted' ) );
47
+ add_action( 'set_user_role', array( $this, 'EventUserRoleChanged' ), 10, 3 );
48
+
49
+ add_action( 'edit_user_profile', array( $this, 'EventOpenProfile' ), 10, 1 );
50
+ }
51
+
52
+ /**
53
+ * List of super admins.
54
+ *
55
+ * @var array
56
+ */
57
+ protected $old_superadmins;
58
+
59
+ /**
60
+ * Triggered when a user accesses the admin area.
61
+ */
62
+ public function EventAdminInit() {
63
+ if ( $this->IsMultisite() ) {
64
+ $this->old_superadmins = get_super_admins();
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Triggered when a user is registered.
70
+ *
71
+ * @param int $user_id - User ID of the registered user.
72
+ */
73
+ public function EventUserRegister( $user_id ) {
74
+ $user = get_userdata( $user_id );
75
+ $ismu = function_exists( 'is_multisite' ) && is_multisite();
76
+ $event = $ismu ? 4012 : (is_user_logged_in() ? 4001 : 4000);
77
+ $current_user = wp_get_current_user();
78
+ $this->plugin->alerts->Trigger(
79
+ $event, array(
80
+ 'NewUserID' => $user_id,
81
+ 'UserChanger' => ! empty( $current_user ) ? $current_user->user_login : '',
82
+ 'NewUserData' => (object) array(
83
+ 'Username' => $user->user_login,
84
+ 'FirstName' => $user->user_firstname,
85
+ 'LastName' => $user->user_lastname,
86
+ 'Email' => $user->user_email,
87
+ 'Roles' => is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles,
88
+ ),
89
+ ), true
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Triggered when a user role is changed.
95
+ *
96
+ * @param int $user_id - User ID of the user.
97
+ * @param string $role - String of new roles.
98
+ * @param string $old_roles - String of old roles.
99
+ */
100
+ public function EventUserRoleChanged( $user_id, $role, $old_roles ) {
101
+ // Filter $_POST array for security.
102
+ $post_array = filter_input_array( INPUT_POST );
103
+
104
+ if ( isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-user_' . $user_id ) ) {
105
+ return false;
106
+ }
107
+
108
+ $user = get_userdata( $user_id );
109
+ $bbpress_roles = array( 'bbp_spectator', 'bbp_moderator', 'bbp_participant', 'bbp_keymaster', 'bbp_blocked' );
110
+ // Remove any BBPress roles.
111
+ if ( is_array( $old_roles ) ) {
112
+ foreach ( $old_roles as $value ) {
113
+ if ( in_array( $value, $bbpress_roles ) ) {
114
+ if ( isset( $post_array['bbp-forums-role'] ) && $post_array['bbp-forums-role'] != $value ) {
115
+ $current_user = wp_get_current_user();
116
+ $this->plugin->alerts->TriggerIf(
117
+ 4013, array(
118
+ 'TargetUsername' => $user->user_login,
119
+ 'OldRole' => ucfirst( substr( $value, 4 ) ),
120
+ 'NewRole' => ( isset( $post_array['bbp-forums-role'] ) ) ? ucfirst( substr( $post_array['bbp-forums-role'], 4 ) ) : false,
121
+ 'UserChanger' => $current_user->user_login,
122
+ )
123
+ );
124
+ }
125
+ }
126
+ }
127
+ $old_roles = array_diff( $old_roles, $bbpress_roles );
128
+ }
129
+ $old_role = count( $old_roles ) ? implode( ', ', $old_roles ) : '';
130
+ $new_role = $role;
131
+ if ( $old_role != $new_role ) {
132
+ $this->plugin->alerts->TriggerIf(
133
+ 4002, array(
134
+ 'TargetUserID' => $user_id,
135
+ 'TargetUsername' => $user->user_login,
136
+ 'OldRole' => $old_role,
137
+ 'NewRole' => $new_role,
138
+ ), array( $this, 'MustNotContainUserChanges' )
139
+ );
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Triggered when a user changes email, password
145
+ * or user is granted or revoked super admin access.
146
+ *
147
+ * @param int $user_id - User ID of the registered user.
148
+ */
149
+ public function EventUserChanged( $user_id ) {
150
+ // Filter $_POST array for security.
151
+ $post_array = filter_input_array( INPUT_POST );
152
+
153
+ // Get user data.
154
+ $user = get_userdata( $user_id );
155
+
156
+ // Password changed.
157
+ if ( ! empty( $post_array['pass1'] ) && ! empty( $post_array['pass2'] ) ) {
158
+ if ( trim( $post_array['pass1'] ) == trim( $post_array['pass2'] ) ) {
159
+ $event = get_current_user_id() == $user_id ? 4003 : 4004;
160
+ $this->plugin->alerts->Trigger(
161
+ $event, array(
162
+ 'TargetUserID' => $user_id,
163
+ 'TargetUserData' => (object) array(
164
+ 'Username' => $user->user_login,
165
+ 'Roles' => is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles,
166
+ ),
167
+ )
168
+ );
169
+ }
170
+ }
171
+
172
+ // Email changed.
173
+ if ( ! empty( $post_array['email'] ) ) {
174
+ $old_email = $user->user_email;
175
+ $new_email = trim( $post_array['email'] );
176
+ if ( $old_email != $new_email ) {
177
+ $event = get_current_user_id() == $user_id ? 4005 : 4006;
178
+ $this->plugin->alerts->Trigger(
179
+ $event, array(
180
+ 'TargetUserID' => $user_id,
181
+ 'TargetUsername' => $user->user_login,
182
+ 'OldEmail' => $old_email,
183
+ 'NewEmail' => $new_email,
184
+ )
185
+ );
186
+ }
187
+ }
188
+
189
+ if ( $this->IsMultisite() ) {
190
+ $username = $user->user_login;
191
+ $enabled = isset( $post_array['super_admin'] );
192
+
193
+ if ( get_current_user_id() != $user_id ) {
194
+ // Super admin enabled.
195
+ if ( $enabled && ! in_array( $username, $this->old_superadmins ) ) {
196
+ $this->plugin->alerts->Trigger(
197
+ 4008, array(
198
+ 'TargetUserID' => $user_id,
199
+ 'TargetUsername' => $user->user_login,
200
+ )
201
+ );
202
+ }
203
+
204
+ // Super admin disabled.
205
+ if ( ! $enabled && in_array( $username, $this->old_superadmins ) ) {
206
+ $this->plugin->alerts->Trigger(
207
+ 4009, array(
208
+ 'TargetUserID' => $user_id,
209
+ 'TargetUsername' => $user->user_login,
210
+ )
211
+ );
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Triggered when a user is deleted.
219
+ *
220
+ * @param int $user_id - User ID of the registered user.
221
+ */
222
+ public function EventUserDeleted( $user_id ) {
223
+ $user = get_userdata( $user_id );
224
+ $role = is_array( $user->roles ) ? implode( ', ', $user->roles ) : $user->roles;
225
+ $this->plugin->alerts->TriggerIf(
226
+ 4007, array(
227
+ 'TargetUserID' => $user_id,
228
+ 'TargetUserData' => (object) array(
229
+ 'Username' => $user->user_login,
230
+ 'FirstName' => $user->user_firstname,
231
+ 'LastName' => $user->user_lastname,
232
+ 'Email' => $user->user_email,
233
+ 'Roles' => $role ? $role : 'none',
234
+ ),
235
+ ), array( $this, 'MustNotContainCreateUser' )
236
+ );
237
+ }
238
+
239
+ /**
240
+ * Triggered when a user profile is opened.
241
+ *
242
+ * @param object $user - Instance of WP_User.
243
+ */
244
+ public function EventOpenProfile( $user ) {
245
+ if ( ! empty( $user ) ) {
246
+ $current_user = wp_get_current_user();
247
+
248
+ // Filter $_GET array for security.
249
+ $get_array = filter_input_array( INPUT_GET );
250
+
251
+ $updated = ( isset( $get_array['updated'] ) ) ? true : false;
252
+ if ( ! empty( $current_user ) && ( $user->ID !== $current_user->ID ) && empty( $updated ) ) {
253
+ $this->plugin->alerts->Trigger(
254
+ 4014, array(
255
+ 'UserChanger' => $current_user->user_login,
256
+ 'TargetUsername' => $user->user_login,
257
+ )
258
+ );
259
+ }
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Must Not Contain Create User.
265
+ *
266
+ * @param WSAL_AlertManager $mgr - Instance of WSAL_AlertManager.
267
+ */
268
+ public function MustNotContainCreateUser( WSAL_AlertManager $mgr ) {
269
+ return ! $mgr->WillTrigger( 4012 );
270
+ }
271
+
272
+ /**
273
+ * Must Not Contain User Changes.
274
+ *
275
+ * @param WSAL_AlertManager $mgr - Instance of WSAL_AlertManager.
276
+ */
277
+ public function MustNotContainUserChanges( WSAL_AlertManager $mgr ) {
278
+ return ! ( $mgr->WillOrHasTriggered( 4010 )
279
+ || $mgr->WillOrHasTriggered( 4011 )
280
+ || $mgr->WillOrHasTriggered( 4012 )
281
+ || $mgr->WillOrHasTriggered( 4000 )
282
+ || $mgr->WillOrHasTriggered( 4001 )
283
+ );
284
+ }
285
  }
classes/Sensors/Widgets.php CHANGED
@@ -1,7 +1,19 @@
1
  <?php
2
  /**
 
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Widgets sensor.
6
  *
7
  * 2042 User added a new widget
@@ -9,256 +21,291 @@
9
  * 2044 User deleted widget
10
  * 2045 User moved widget
11
  * 2071 User changed widget position
 
 
 
12
  */
13
- class WSAL_Sensors_Widgets extends WSAL_AbstractSensor
14
- {
15
- protected $_WidgetMoveData = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
 
17
- /**
18
- * Listening to events using WP hooks.
19
- */
20
- public function HookEvents()
21
- {
22
- if (current_user_can("edit_theme_options")) {
23
- add_action('admin_init', array($this, 'EventWidgetMove'));
24
- add_action('admin_init', array($this, 'EventWidgetPostMove'));
25
- }
26
- add_action('sidebar_admin_setup', array($this, 'EventWidgetActivity'));
27
- }
28
-
29
- /**
30
- * Triggered when a user accesses the admin area.
31
- * Moved widget.
32
- */
33
- public function EventWidgetMove()
34
- {
35
- if (isset($_POST) && !empty($_POST['sidebars'])) {
36
- $crtSidebars = $_POST['sidebars'];
37
- $sidebars = array();
38
- foreach ($crtSidebars as $key => $val) {
39
- $sb = array();
40
- if (!empty($val)) {
41
- $val = explode(',', $val);
42
- foreach ($val as $k => $v) {
43
- if (strpos($v, 'widget-') === false) {
44
- continue;
45
- }
46
- $sb[$k] = substr($v, strpos($v, '_') + 1);
47
- }
48
- }
49
- $sidebars[$key] = $sb;
50
- }
51
- $crtSidebars = $sidebars;
52
- $dbSidebars = get_option('sidebars_widgets');
53
- $wName = $fromSidebar = $toSidebar = '';
54
- foreach ($crtSidebars as $sidebarName => $values) {
55
- if (is_array($values) && ! empty($values) && isset($dbSidebars[$sidebarName])) {
56
- foreach ($values as $widgetName) {
57
- if (! in_array($widgetName, $dbSidebars[$sidebarName])) {
58
- $toSidebar = $sidebarName;
59
- $wName = $widgetName;
60
- foreach ($dbSidebars as $name => $v) {
61
- if (is_array($v) && !empty($v) && in_array($widgetName, $v)) {
62
- $fromSidebar = $name;
63
- continue;
64
- }
65
- }
66
- }
67
- }
68
- }
69
- }
70
-
71
- if (empty($wName) || empty($fromSidebar) || empty($toSidebar)) {
72
- return;
73
- }
74
 
75
- if (preg_match('/^sidebar-/', $fromSidebar) || preg_match('/^sidebar-/', $toSidebar)) {
76
- // This option will hold the data needed to trigger the event 2045
77
- // as at this moment the $wp_registered_sidebars variable is not yet populated
78
- // so we cannot retrieve the name for sidebar-1 || sidebar-2
79
- // we will then check for this variable in the EventWidgetPostMove() event
80
- $this->_WidgetMoveData = array('widget' => $wName, 'from' => $fromSidebar, 'to' => $toSidebar);
81
- return;
82
- }
83
 
84
- $this->plugin->alerts->Trigger(2045, array(
85
- 'WidgetName' => $wName,
86
- 'OldSidebar' => $fromSidebar,
87
- 'NewSidebar' => $toSidebar,
88
- ));
89
- }
90
- }
91
-
92
- /**
93
- * Triggered when a user accesses the admin area.
94
- * Changed widget position or moved widget.
95
- */
96
- public function EventWidgetPostMove()
97
- {
98
- // #!-- generates the event 2071
99
- if (isset($_REQUEST['action']) && ($_REQUEST['action']=='widgets-order')) {
100
- if (isset($_REQUEST['sidebars'])) {
101
- // Get the sidebars from $_REQUEST
102
- $requestSidebars = array();
103
- if ($_REQUEST['sidebars']) {
104
- foreach ($_REQUEST['sidebars'] as $key => &$value) {
105
- if (!empty($value)) {
106
- // build the sidebars array
107
- $value = explode(',', $value);
108
- // Cleanup widgets' name
109
- foreach ($value as $k => &$widgetName) {
110
- $widgetName = preg_replace("/^([a-z]+-[0-9]+)+?_/i", '', $widgetName);
111
- }
112
- $requestSidebars[$key] = $value;
113
- }
114
- }
115
- }
116
 
117
- if ($requestSidebars) {
118
- // Get the sidebars from DATABASE
119
- $sidebar_widgets = wp_get_sidebars_widgets();
120
- // Get global sidebars so we can retrieve the real name of the sidebar
121
- global $wp_registered_sidebars;
122
 
123
- // Check in each array if there's any change
124
- foreach ($requestSidebars as $sidebarName => $widgets) {
125
- if (isset($sidebar_widgets[$sidebarName])) {
126
- foreach ($sidebar_widgets[$sidebarName] as $i => $widgetName) {
127
- $index = array_search($widgetName, $widgets);
128
- // check to see whether or not the widget has been moved
129
- if ($i != $index) {
130
- $sn = $sidebarName;
131
- // Try to retrieve the real name of the sidebar, otherwise fall-back to id: $sidebarName
132
- if ($wp_registered_sidebars && isset($wp_registered_sidebars[$sidebarName])) {
133
- $sn = $wp_registered_sidebars[$sidebarName]['name'];
134
- }
135
- $this->plugin->alerts->Trigger(2071, array(
136
- 'WidgetName' => $widgetName,
137
- 'OldPosition' => $i+1,
138
- 'NewPosition' => $index+1,
139
- 'Sidebar' => $sn,
140
- ));
141
- }
142
- }
143
- }
144
- }
145
- }
146
- }
147
- }
148
- // #!--
149
 
150
- if ($this->_WidgetMoveData) {
151
- $wName = $this->_WidgetMoveData['widget'];
152
- $fromSidebar = $this->_WidgetMoveData['from'];
153
- $toSidebar = $this->_WidgetMoveData['to'];
154
-
155
- global $wp_registered_sidebars;
156
-
157
- if (preg_match('/^sidebar-/', $fromSidebar)) {
158
- $fromSidebar = isset($wp_registered_sidebars[$fromSidebar])
159
- ? $wp_registered_sidebars[$fromSidebar]['name']
160
- : $fromSidebar
161
- ;
162
- }
163
-
164
- if (preg_match('/^sidebar-/', $toSidebar)) {
165
- $toSidebar = isset($wp_registered_sidebars[$toSidebar])
166
- ? $wp_registered_sidebars[$toSidebar]['name']
167
- : $toSidebar
168
- ;
169
- }
170
-
171
- $this->plugin->alerts->Trigger(2045, array(
172
- 'WidgetName' => $wName,
173
- 'OldSidebar' => $fromSidebar,
174
- 'NewSidebar' => $toSidebar,
175
- ));
176
- }
177
- }
178
-
179
- /**
180
- * Widgets Activity (added, modified, deleted).
181
- */
182
- public function EventWidgetActivity()
183
- {
184
- if (!isset($_POST) || !isset($_POST['widget-id']) || empty($_POST['widget-id'])) {
185
- return;
186
- }
187
-
188
- $postData = $_POST;
189
- global $wp_registered_sidebars;
190
- $canCheckSidebar = (empty($wp_registered_sidebars) ? false : true);
191
-
192
- switch (true) {
193
- // added widget
194
- case isset($postData['add_new']) && $postData['add_new'] == 'multi':
195
- $sidebar = isset($postData['sidebar']) ? $postData['sidebar'] : null;
196
- if ($canCheckSidebar && preg_match('/^sidebar-/', $sidebar)) {
197
- $sidebar = $wp_registered_sidebars[$sidebar]['name'];
198
- }
199
- $this->plugin->alerts->Trigger(2042, array(
200
- 'WidgetName' => $postData['id_base'],
201
- 'Sidebar' => $sidebar,
202
- ));
203
- break;
204
- // deleted widget
205
- case isset($postData['delete_widget']) && intval($postData['delete_widget']) == 1:
206
- $sidebar = isset($postData['sidebar']) ? $postData['sidebar'] : null;
207
- if ($canCheckSidebar && preg_match('/^sidebar-/', $sidebar)) {
208
- $sidebar = $wp_registered_sidebars[$sidebar]['name'];
209
- }
210
- $this->plugin->alerts->Trigger(2044, array(
211
- 'WidgetName' => $postData['id_base'],
212
- 'Sidebar' => $sidebar,
213
- ));
214
- break;
215
- // modified widget
216
- case isset($postData['id_base']) && !empty($postData['id_base']):
217
- $wId = 0;
218
- if (!empty($postData['multi_number'])) {
219
- $wId = intval($postData['multi_number']);
220
- } elseif (!empty($postData['widget_number'])) {
221
- $wId = intval($postData['widget_number']);
222
- }
223
- if (empty($wId)) {
224
- return;
225
- }
226
 
227
- $wName = $postData['id_base'];
228
- $sidebar = isset($postData['sidebar']) ? $postData['sidebar'] : null;
229
- $wData = isset($postData["widget-$wName"][$wId])
230
- ? $postData["widget-$wName"][$wId]
231
- : null;
232
 
233
- if (empty($wData)) {
234
- return;
235
- }
236
- // get info from db
237
- $wdbData = get_option("widget_".$wName);
238
- if (empty($wdbData[$wId])) {
239
- return;
240
- }
241
 
242
- // transform 'on' -> 1
243
- foreach ($wData as $k => $v) {
244
- if ($v == 'on') {
245
- $wData[$k] = 1;
246
- }
247
- }
248
 
249
- // compare - checks for any changes inside widgets
250
- $diff = array_diff_assoc($wData, $wdbData[$wId]);
251
- $count = count($diff);
252
- if ($count > 0) {
253
- if ($canCheckSidebar && preg_match("/^sidebar-/", $sidebar)) {
254
- $sidebar = $wp_registered_sidebars[$sidebar]['name'];
255
- }
256
- $this->plugin->alerts->Trigger(2043, array(
257
- 'WidgetName' => $wName,
258
- 'Sidebar' => $sidebar,
259
- ));
260
- }
261
- break;
262
- }
263
- }
 
 
264
  }
1
  <?php
2
  /**
3
+ * Sensor: Widgets
4
+ *
5
+ * Widgets sensor class file.
6
+ *
7
+ * @since 1.0.0
8
  * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Widgets sensor.
18
  *
19
  * 2042 User added a new widget
21
  * 2044 User deleted widget
22
  * 2045 User moved widget
23
  * 2071 User changed widget position
24
+ *
25
+ * @package Wsal
26
+ * @subpackage Sensors
27
  */
28
+ class WSAL_Sensors_Widgets extends WSAL_AbstractSensor {
29
+
30
+ /**
31
+ * Widget Move Data
32
+ *
33
+ * @var array
34
+ */
35
+ protected $_widget_move_data = null;
36
+
37
+ /**
38
+ * Listening to events using WP hooks.
39
+ */
40
+ public function HookEvents() {
41
+ if ( current_user_can( 'edit_theme_options' ) ) {
42
+ add_action( 'admin_init', array( $this, 'EventWidgetMove' ) );
43
+ add_action( 'admin_init', array( $this, 'EventWidgetPostMove' ) );
44
+ }
45
+ add_action( 'sidebar_admin_setup', array( $this, 'EventWidgetActivity' ) );
46
+ }
47
+
48
+ /**
49
+ * Triggered when a user accesses the admin area.
50
+ * Moved widget.
51
+ */
52
+ public function EventWidgetMove() {
53
+ // Filter $_POST array for security.
54
+ $post_array = filter_input_array( INPUT_POST );
55
+
56
+ if ( isset( $post_array['savewidgets'] ) ) {
57
+ check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
58
+ }
59
+
60
+ if ( isset( $post_array ) && ! empty( $post_array['sidebars'] ) ) {
61
+ $current_sidebars = $post_array['sidebars'];
62
+ $sidebars = array();
63
+ foreach ( $current_sidebars as $key => $val ) {
64
+ $sb = array();
65
+ if ( ! empty( $val ) ) {
66
+ $val = explode( ',', $val );
67
+ foreach ( $val as $k => $v ) {
68
+ if ( strpos( $v, 'widget-' ) === false ) {
69
+ continue;
70
+ }
71
+ $sb[ $k ] = substr( $v, strpos( $v, '_' ) + 1 );
72
+ }
73
+ }
74
+ $sidebars[ $key ] = $sb;
75
+ }
76
+ $current_sidebars = $sidebars;
77
+ $db_sidebars = get_option( 'sidebars_widgets' );
78
+ $widget_name = $from_sidebar = $to_sidebar = '';
79
+ foreach ( $current_sidebars as $sidebar_name => $values ) {
80
+ if ( is_array( $values ) && ! empty( $values ) && isset( $db_sidebars[ $sidebar_name ] ) ) {
81
+ foreach ( $values as $widget_name ) {
82
+ if ( ! in_array( $widget_name, $db_sidebars[ $sidebar_name ] ) ) {
83
+ $to_sidebar = $sidebar_name;
84
+ $widget_name = $widget_name;
85
+ foreach ( $db_sidebars as $name => $v ) {
86
+ if ( is_array( $v ) && ! empty( $v ) && in_array( $widget_name, $v ) ) {
87
+ $from_sidebar = $name;
88
+ continue;
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ if ( empty( $widget_name ) || empty( $from_sidebar ) || empty( $to_sidebar ) ) {
97
+ return;
98
+ }
99
+
100
+ if ( preg_match( '/^sidebar-/', $from_sidebar ) || preg_match( '/^sidebar-/', $to_sidebar ) ) {
101
+ // This option will hold the data needed to trigger the event 2045
102
+ // as at this moment the $wp_registered_sidebars variable is not yet populated
103
+ // so we cannot retrieve the name for sidebar-1 || sidebar-2
104
+ // we will then check for this variable in the EventWidgetPostMove() event.
105
+ $this->_widget_move_data = array(
106
+ 'widget' => $widget_name,
107
+ 'from' => $from_sidebar,
108
+ 'to' => $to_sidebar,
109
+ );
110
+ return;
111
+ }
112
+
113
+ $this->plugin->alerts->Trigger(
114
+ 2045, array(
115
+ 'WidgetName' => $widget_name,
116
+ 'OldSidebar' => $from_sidebar,
117
+ 'NewSidebar' => $to_sidebar,
118
+ )
119
+ );
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Triggered when a user accesses the admin area.
125
+ * Changed widget position or moved widget.
126
+ */
127
+ public function EventWidgetPostMove() {
128
+ // Filter $_POST array for security.
129
+ $post_array = filter_input_array( INPUT_POST );
130
+
131
+ // #!-- generates the event 2071
132
+ if ( isset( $post_array['action'] ) && ('widgets-order' == $post_array['action']) ) {
133
+ if ( isset( $post_array['sidebars'] ) ) {
134
+ // Get the sidebars from $post_array.
135
+ $request_sidebars = array();
136
+ if ( $post_array['sidebars'] ) {
137
+ foreach ( $post_array['sidebars'] as $key => &$value ) {
138
+ if ( ! empty( $value ) ) {
139
+ // Build the sidebars array.
140
+ $value = explode( ',', $value );
141
+ // Cleanup widgets' name.
142
+ foreach ( $value as $k => &$widget_name ) {
143
+ $widget_name = preg_replace( '/^([a-z]+-[0-9]+)+?_/i', '', $widget_name );
144
+ }
145
+ $request_sidebars[ $key ] = $value;
146
+ }
147
+ }
148
+ }
149
+
150
+ if ( $request_sidebars ) {
151
+ // Get the sidebars from DATABASE.
152
+ $sidebar_widgets = wp_get_sidebars_widgets();
153
+ // Get global sidebars so we can retrieve the real name of the sidebar.
154
+ global $wp_registered_sidebars;
155
+
156
+ // Check in each array if there's any change.
157
+ foreach ( $request_sidebars as $sidebar_name => $widgets ) {
158
+ if ( isset( $sidebar_widgets[ $sidebar_name ] ) ) {
159
+ foreach ( $sidebar_widgets[ $sidebar_name ] as $i => $widget_name ) {
160
+ $index = array_search( $widget_name, $widgets );
161
+ // Check to see whether or not the widget has been moved.
162
+ if ( $i != $index ) {
163
+ $sn = $sidebar_name;
164
+ // Try to retrieve the real name of the sidebar, otherwise fall-back to id: $sidebar_name.
165
+ if ( $wp_registered_sidebars && isset( $wp_registered_sidebars[ $sidebar_name ] ) ) {
166
+ $sn = $wp_registered_sidebars[ $sidebar_name ]['name'];
167
+ }
168
+ $this->plugin->alerts->Trigger(
169
+ 2071, array(
170
+ 'WidgetName' => $widget_name,
171
+ 'OldPosition' => $i + 1,
172
+ 'NewPosition' => $index + 1,
173
+ 'Sidebar' => $sn,
174
+ )
175
+ );
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ // #!--
184
+ if ( $this->_widget_move_data ) {
185
+ $widget_name = $this->_widget_move_data['widget'];
186
+ $from_sidebar = $this->_widget_move_data['from'];
187
+ $to_sidebar = $this->_widget_move_data['to'];
188
+
189
+ global $wp_registered_sidebars;
190
+
191
+ if ( preg_match( '/^sidebar-/', $from_sidebar ) ) {
192
+ $from_sidebar = isset( $wp_registered_sidebars[ $from_sidebar ] )
193
+ ? $wp_registered_sidebars[ $from_sidebar ]['name']
194
+ : $from_sidebar
195
+ ;
196
+ }
197
+
198
+ if ( preg_match( '/^sidebar-/', $to_sidebar ) ) {
199
+ $to_sidebar = isset( $wp_registered_sidebars[ $to_sidebar ] )
200
+ ? $wp_registered_sidebars[ $to_sidebar ]['name']
201
+ : $to_sidebar
202
+ ;
203
+ }
204
 
205
+ $this->plugin->alerts->Trigger(
206
+ 2045, array(
207
+ 'WidgetName' => $widget_name,
208
+ 'OldSidebar' => $from_sidebar,
209
+ 'NewSidebar' => $to_sidebar,
210
+ )
211
+ );
212
+ }
213
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
+ /**
216
+ * Widgets Activity (added, modified, deleted).
217
+ */
218
+ public function EventWidgetActivity() {
219
+ // Filter $_POST array for security.
220
+ $post_array = filter_input_array( INPUT_POST );
 
 
221
 
222
+ if ( ! isset( $post_array ) || ! isset( $post_array['widget-id'] ) || empty( $post_array['widget-id'] ) ) {
223
+ return;
224
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
+ if ( isset( $post_array['savewidgets'] ) ) {
227
+ check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' );
228
+ }
 
 
229
 
230
+ global $wp_registered_sidebars;
231
+ $can_check_sidebar = (empty( $wp_registered_sidebars ) ? false : true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
+ switch ( true ) {
234
+ // Added widget.
235
+ case isset( $post_array['add_new'] ) && 'multi' == $post_array['add_new']:
236
+ $sidebar = isset( $post_array['sidebar'] ) ? $post_array['sidebar'] : null;
237
+ if ( $can_check_sidebar && preg_match( '/^sidebar-/', $sidebar ) ) {
238
+ $sidebar = $wp_registered_sidebars[ $sidebar ]['name'];
239
+ }
240
+ $this->plugin->alerts->Trigger(
241
+ 2042, array(
242
+ 'WidgetName' => $post_array['id_base'],
243
+ 'Sidebar' => $sidebar,
244
+ )
245
+ );
246
+ break;
247
+ // Deleted widget.
248
+ case isset( $post_array['delete_widget'] ) && intval( $post_array['delete_widget'] ) == 1:
249
+ $sidebar = isset( $post_array['sidebar'] ) ? $post_array['sidebar'] : null;
250
+ if ( $can_check_sidebar && preg_match( '/^sidebar-/', $sidebar ) ) {
251
+ $sidebar = $wp_registered_sidebars[ $sidebar ]['name'];
252
+ }
253
+ $this->plugin->alerts->Trigger(
254
+ 2044, array(
255
+ 'WidgetName' => $post_array['id_base'],
256
+ 'Sidebar' => $sidebar,
257
+ )
258
+ );
259
+ break;
260
+ // Modified widget.
261
+ case isset( $post_array['id_base'] ) && ! empty( $post_array['id_base'] ):
262
+ $widget_id = 0;
263
+ if ( ! empty( $post_array['multi_number'] ) ) {
264
+ $widget_id = intval( $post_array['multi_number'] );
265
+ } elseif ( ! empty( $post_array['widget_number'] ) ) {
266
+ $widget_id = intval( $post_array['widget_number'] );
267
+ }
268
+ if ( empty( $widget_id ) ) {
269
+ return;
270
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
+ $widget_name = $post_array['id_base'];
273
+ $sidebar = isset( $post_array['sidebar'] ) ? $post_array['sidebar'] : null;
274
+ $widget_data = isset( $post_array[ "widget-$widget_name" ][ $widget_id ] )
275
+ ? $post_array[ "widget-$widget_name" ][ $widget_id ]
276
+ : null;
277
 
278
+ if ( empty( $widget_data ) ) {
279
+ return;
280
+ }
281
+ // Get info from db.
282
+ $widget_db_data = get_option( 'widget_' . $widget_name );
283
+ if ( empty( $widget_db_data[ $widget_id ] ) ) {
284
+ return;
285
+ }
286
 
287
+ // Transform 'on' -> 1.
288
+ foreach ( $widget_data as $k => $v ) {
289
+ if ( 'on' == $v ) {
290
+ $widget_data[ $k ] = 1;
291
+ }
292
+ }
293
 
294
+ // Compare - checks for any changes inside widgets.
295
+ $diff = array_diff_assoc( $widget_data, $widget_db_data[ $widget_id ] );
296
+ $count = count( $diff );
297
+ if ( $count > 0 ) {
298
+ if ( $can_check_sidebar && preg_match( '/^sidebar-/', $sidebar ) ) {
299
+ $sidebar = $wp_registered_sidebars[ $sidebar ]['name'];
300
+ }
301
+ $this->plugin->alerts->Trigger(
302
+ 2043, array(
303
+ 'WidgetName' => $widget_name,
304
+ 'Sidebar' => $sidebar,
305
+ )
306
+ );
307
+ }
308
+ break;
309
+ }
310
+ }
311
  }
classes/Sensors/WooCommerce.php CHANGED
@@ -1,7 +1,18 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
4
- * @subpackage Sensors
 
 
 
 
 
 
 
5
  * Support for WooCommerce Plugin.
6
  *
7
  * 9000 User created a new product
@@ -38,863 +49,1339 @@
38
  * 9031 User changed the currency
39
  * 9032 User Enabled/Disabled the use of coupons during checkout
40
  * 9033 User Enabled/Disabled guest checkout
 
 
 
41
  */
42
- class WSAL_Sensors_WooCommerce extends WSAL_AbstractSensor
43
- {
44
- protected $_OldPost = null;
45
- protected $_OldLink = null;
46
- protected $_OldCats = null;
47
- protected $_OldData = null;
48
- protected $_OldStockStatus = null;
49
- protected $_OldFileNames = array();
50
- protected $_OldFileUrls = array();
51
-
52
- /**
53
- * Listening to events using WP hooks.
54
- */
55
- public function HookEvents()
56
- {
57
- if (current_user_can("edit_posts")) {
58
- add_action('admin_init', array($this, 'EventAdminInit'));
59
- }
60
- add_action('post_updated', array($this, 'EventChanged'), 10, 3);
61
- add_action('delete_post', array($this, 'EventDeleted'), 10, 1);
62
- add_action('wp_trash_post', array($this, 'EventTrashed'), 10, 1);
63
- add_action('untrash_post', array($this, 'EventUntrashed'));
64
-
65
- add_action('create_product_cat', array($this, 'EventCategoryCreation'), 10, 1);
66
- // add_action('edit_product_cat', array($this, 'EventCategoryChanged'), 10, 1);
67
- }
68
-
69
- /**
70
- * Triggered when a user accesses the admin area.
71
- */
72
- public function EventAdminInit()
73
- {
74
- // load old data, if applicable
75
- $this->RetrieveOldData();
76
- $this->CheckSettingsChange();
77
- }
78
-
79
- /**
80
- * Retrieve Old data.
81
- * @global mixed $_POST post data
82
- */
83
- protected function RetrieveOldData()
84
- {
85
- if (isset($_POST) && isset($_POST['post_ID'])
86
- && !(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
87
- && !(isset($_POST['action']) && $_POST['action'] == 'autosave')
88
- ) {
89
- $postID = intval($_POST['post_ID']);
90
- $this->_OldPost = get_post($postID);
91
- $this->_OldLink = get_post_permalink($postID, false, true);
92
- $this->_OldCats = $this->GetProductCategories($this->_OldPost);
93
- $this->_OldData = $this->GetProductData($this->_OldPost);
94
- $this->_OldStockStatus = get_post_meta($postID, '_stock_status', true);
95
-
96
- $oldDownloadableFiles = get_post_meta($postID, '_downloadable_files', true);
97
- if (!empty($oldDownloadableFiles)) {
98
- foreach ($oldDownloadableFiles as $file) {
99
- array_push($this->_OldFileNames, $file['name']);
100
- array_push($this->_OldFileUrls, $file['file']);
101
- }
102
- }
103
- }
104
- }
105
-
106
- public function EventChanged($post_ID, $newpost, $oldpost)
107
- {
108
- if ($this->CheckWooCommerce($oldpost)) {
109
- $changes = 0 + $this->EventCreation($oldpost, $newpost);
110
- if (!$changes) {
111
- // Change Categories
112
- $changes = $this->CheckCategoriesChange($this->_OldCats, $this->GetProductCategories($newpost), $oldpost, $newpost);
113
- }
114
- if (!$changes) {
115
- // Change Short description, Text, URL, Product Data, Date, Visibility, etc.
116
- $changes = 0
117
- + $this->CheckShortDescriptionChange($oldpost, $newpost)
118
- + $this->CheckTextChange($oldpost, $newpost)
119
- + $this->CheckProductDataChange($this->_OldData, $newpost)
120
- + $this->CheckDateChange($oldpost, $newpost)
121
- + $this->CheckVisibilityChange($oldpost)
122
- + $this->CheckStatusChange($oldpost, $newpost)
123
- + $this->CheckPriceChange($oldpost)
124
- + $this->CheckSKUChange($oldpost)
125
- + $this->CheckStockStatusChange($oldpost)
126
- + $this->CheckStockQuantityChange($oldpost)
127
- + $this->CheckTypeChange($oldpost, $newpost)
128
- + $this->CheckWeightChange($oldpost)
129
- + $this->CheckDimensionsChange($oldpost)
130
- + $this->CheckDownloadableFileChange($oldpost)
131
- ;
132
- }
133
- if (!$changes) {
134
- // Change Permalink
135
- $changes = $this->CheckPermalinkChange($this->_OldLink, get_post_permalink($post_ID, false, true), $newpost);
136
- if (!$changes) {
137
- // if no one of the above changes happen
138
- $this->CheckModifyChange($oldpost, $newpost);
139
- }
140
- }
141
- }
142
- }
143
-
144
- /**
145
- * Trigger events 9000, 9001
146
- */
147
- private function EventCreation($old_post, $new_post)
148
- {
149
- $original = isset($_POST['original_post_status']) ? $_POST['original_post_status'] : '';
150
- if ($original == 'draft' && $new_post->post_status == 'draft') {
151
- return 0;
152
- }
153
- if ($old_post->post_status == 'draft' || $original == 'auto-draft') {
154
- if ($old_post->post_type == 'product') {
155
- $editorLink = $this->GetEditorLink($new_post);
156
- if ($new_post->post_status == 'draft') {
157
- $this->plugin->alerts->Trigger(9000, array(
158
- 'ProductTitle' => $new_post->post_title,
159
- $editorLink['name'] => $editorLink['value']
160
- ));
161
- return 1;
162
- } else if ($new_post->post_status == 'publish') {
163
- $this->plugin->alerts->Trigger(9001, array(
164
- 'ProductTitle' => $new_post->post_title,
165
- 'ProductUrl' => get_post_permalink($new_post->ID),
166
- $editorLink['name'] => $editorLink['value']
167
- ));
168
- return 1;
169
- }
170
- }
171
- }
172
- return 0;
173
- }
174
-
175
- /**
176
- * Trigger events 9002
177
- */
178
- public function EventCategoryCreation($term_id = null)
179
- {
180
- $term = get_term($term_id);
181
- if (!empty($term)) {
182
- $this->plugin->alerts->Trigger(9002, array(
183
- 'CategoryName' => $term->name,
184
- 'Slug' => $term->slug
185
- ));
186
- }
187
- }
188
-
189
- /**
190
- * Not implemented
191
- */
192
- public function EventCategoryChanged($term_id = null)
193
- {
194
- $old_term = get_term($term_id);
195
- if (isset($_POST['taxonomy'])) {
196
- // new $term in $_POST
197
- }
198
- }
199
-
200
- /**
201
- * Trigger events 9003
202
- */
203
- protected function CheckCategoriesChange($oldCats, $newCats, $oldpost, $newpost)
204
- {
205
- if ($newpost->post_status == 'trash' || $oldpost->post_status == 'trash') {
206
- return 0;
207
- }
208
- $oldCats = is_array($oldCats) ? implode(', ', $oldCats) : $oldCats;
209
- $newCats = is_array($newCats) ? implode(', ', $newCats) : $newCats;
210
- if ($oldCats != $newCats) {
211
- $editorLink = $this->GetEditorLink($newpost);
212
- $this->plugin->alerts->Trigger(9003, array(
213
- 'ProductTitle' => $newpost->post_title,
214
- 'OldCategories' => $oldCats ? $oldCats : 'no categories',
215
- 'NewCategories' => $newCats ? $newCats : 'no categories',
216
- $editorLink['name'] => $editorLink['value']
217
- ));
218
- return 1;
219
- }
220
- return 0;
221
- }
222
-
223
- /**
224
- * Trigger events 9004
225
- */
226
- protected function CheckShortDescriptionChange($oldpost, $newpost)
227
- {
228
- if ($oldpost->post_status == 'auto-draft') {
229
- return 0;
230
- }
231
- if ($oldpost->post_excerpt != $newpost->post_excerpt) {
232
- $editorLink = $this->GetEditorLink($oldpost);
233
- $this->plugin->alerts->Trigger(9004, array(
234
- 'ProductTitle' => $oldpost->post_title,
235
- $editorLink['name'] => $editorLink['value']
236
- ));
237
- return 1;
238
- }
239
- return 0;
240
- }
241
-
242
- /**
243
- * Trigger events 9005
244
- */
245
- protected function CheckTextChange($oldpost, $newpost)
246
- {
247
- if ($oldpost->post_status == 'auto-draft') {
248
- return 0;
249
- }
250
- if ($oldpost->post_content != $newpost->post_content) {
251
- $editorLink = $this->GetEditorLink($oldpost);
252
- $this->plugin->alerts->Trigger(9005, array(
253
- 'ProductTitle' => $oldpost->post_title,
254
- $editorLink['name'] => $editorLink['value']
255
- ));
256
- return 1;
257
- }
258
- return 0;
259
- }
260
-
261
- /**
262
- * Trigger events 9006
263
- */
264
- protected function CheckPermalinkChange($oldLink, $newLink, $post)
265
- {
266
- if (($oldLink && $newLink) && ($oldLink != $newLink)) {
267
- $editorLink = $this->GetEditorLink($post);
268
- $this->plugin->alerts->Trigger(9006, array(
269
- 'ProductTitle' => $post->post_title,
270
- 'OldUrl' => $oldLink,
271
- 'NewUrl' => $newLink,
272
- $editorLink['name'] => $editorLink['value']
273
- ));
274
- return 1;
275
- }
276
- return 0;
277
- }
278
-
279
- /**
280
- * Trigger events 9007
281
- */
282
- protected function CheckProductDataChange($oldData, $post)
283
- {
284
- if (isset($_POST['product-type'])) {
285
- $oldData = is_array($oldData) ? implode(', ', $oldData) : $oldData;
286
- $newData = $_POST['product-type'];
287
- if ($oldData != $newData) {
288
- $editorLink = $this->GetEditorLink($post);
289
- $this->plugin->alerts->Trigger(9007, array(
290
- 'ProductTitle' => $post->post_title,
291
- $editorLink['name'] => $editorLink['value']
292
- ));
293
- return 1;
294
- }
295
- }
296
- return 0;
297
- }
298
-
299
- /**
300
- * Trigger events 9008
301
- */
302
- protected function CheckDateChange($oldpost, $newpost)
303
- {
304
- if ($oldpost->post_status == 'draft' || $oldpost->post_status == 'auto-draft') {
305
- return 0;
306
- }
307
- $from = strtotime($oldpost->post_date);
308
- $to = strtotime($newpost->post_date);
309
- if ($from != $to) {
310
- $editorLink = $this->GetEditorLink($oldpost);
311
- $this->plugin->alerts->Trigger(9008, array(
312
- 'ProductTitle' => $oldpost->post_title,
313
- 'OldDate' => $oldpost->post_date,
314
- 'NewDate' => $newpost->post_date,
315
- $editorLink['name'] => $editorLink['value']
316
- ));
317
- return 1;
318
- }
319
- return 0;
320
- }
321
-
322
- /**
323
- * Trigger events 9009
324
- */
325
- protected function CheckVisibilityChange($oldpost)
326
- {
327
- $oldVisibility = isset($_POST['hidden_post_visibility']) ? $_POST['hidden_post_visibility'] : null;
328
- $newVisibility = isset($_POST['visibility']) ? $_POST['visibility'] : null;
329
-
330
- if ($oldVisibility == 'password') {
331
- $oldVisibility = __('Password Protected', 'wp-security-audit-log');
332
- } else {
333
- $oldVisibility = ucfirst($oldVisibility);
334
- }
335
-
336
- if ($newVisibility == 'password') {
337
- $newVisibility = __('Password Protected', 'wp-security-audit-log');
338
- } else {
339
- $newVisibility = ucfirst($newVisibility);
340
- }
341
-
342
- if (($oldVisibility && $newVisibility) && ($oldVisibility != $newVisibility)) {
343
- $editorLink = $this->GetEditorLink($oldpost);
344
- $this->plugin->alerts->Trigger(9009, array(
345
- 'ProductTitle' => $oldpost->post_title,
346
- 'OldVisibility' => $oldVisibility,
347
- 'NewVisibility' => $newVisibility,
348
- $editorLink['name'] => $editorLink['value']
349
- ));
350
- return 1;
351
- }
352
- return 0;
353
- }
354
-
355
- /**
356
- * Trigger events 9010, 9011
357
- */
358
- protected function CheckModifyChange($oldpost, $newpost)
359
- {
360
- if ($newpost->post_status == 'trash') {
361
- return 0;
362
- }
363
- $editorLink = $this->GetEditorLink($oldpost);
364
- if ($oldpost->post_status == 'publish') {
365
- $this->plugin->alerts->Trigger(9010, array(
366
- 'ProductTitle' => $oldpost->post_title,
367
- 'ProductUrl' => get_post_permalink($oldpost->ID),
368
- $editorLink['name'] => $editorLink['value']
369
- ));
370
- } else if ($oldpost->post_status == 'draft') {
371
- $this->plugin->alerts->Trigger(9011, array(
372
- 'ProductTitle' => $oldpost->post_title,
373
- $editorLink['name'] => $editorLink['value']
374
- ));
375
- }
376
- }
377
-
378
- /**
379
- * Moved to Trash 9012
380
- */
381
- public function EventTrashed($post_id)
382
- {
383
- $post = get_post($post_id);
384
- if ($this->CheckWooCommerce($post)) {
385
- $this->plugin->alerts->Trigger(9012, array(
386
- 'ProductTitle' => $post->post_title,
387
- 'ProductUrl' => get_post_permalink($post->ID)
388
- ));
389
- }
390
- }
391
-
392
- /**
393
- * Permanently deleted 9013
394
- */
395
- public function EventDeleted($post_id)
396
- {
397
- $post = get_post($post_id);
398
- if ($this->CheckWooCommerce($post)) {
399
- $this->plugin->alerts->Trigger(9013, array(
400
- 'ProductTitle' => $post->post_title
401
- ));
402
- }
403
- }
404
-
405
- /**
406
- * Restored from Trash 9014
407
- */
408
- public function EventUntrashed($post_id)
409
- {
410
- $post = get_post($post_id);
411
- if ($this->CheckWooCommerce($post)) {
412
- $editorLink = $this->GetEditorLink($post);
413
- $this->plugin->alerts->Trigger(9014, array(
414
- 'ProductTitle' => $post->post_title,
415
- $editorLink['name'] => $editorLink['value']
416
- ));
417
- }
418
- }
419
-
420
- /**
421
- * Trigger events 9015
422
- */
423
- protected function CheckStatusChange($oldpost, $newpost)
424
- {
425
- if ($oldpost->post_status == 'draft' || $oldpost->post_status == 'auto-draft') {
426
- return 0;
427
- }
428
- if ($oldpost->post_status != $newpost->post_status) {
429
- if ($oldpost->post_status != 'trash' && $newpost->post_status != 'trash') {
430
- $editorLink = $this->GetEditorLink($oldpost);
431
- $this->plugin->alerts->Trigger(9015, array(
432
- 'ProductTitle' => $oldpost->post_title,
433
- 'OldStatus' => $oldpost->post_status,
434
- 'NewStatus' => $newpost->post_status,
435
- $editorLink['name'] => $editorLink['value']
436
- ));
437
- return 1;
438
- }
439
- }
440
- return 0;
441
- }
442
-
443
- /**
444
- * Trigger events 9016
445
- */
446
- protected function CheckPriceChange($oldpost)
447
- {
448
- $result = 0;
449
- $oldPrice = get_post_meta($oldpost->ID, '_regular_price', true);
450
- $oldSalePrice = get_post_meta($oldpost->ID, '_sale_price', true);
451
- $newPrice = isset($_POST['_regular_price']) ? $_POST['_regular_price'] : null;
452
- $newSalePrice = isset($_POST['_sale_price']) ? $_POST['_sale_price'] : null;
453
-
454
- if (($newPrice) && ($oldPrice != $newPrice)) {
455
- $result = $this->EventPrice($oldpost, 'Regular price', $oldPrice, $newPrice);
456
- }
457
- if (($newSalePrice) && ($oldSalePrice != $newSalePrice)) {
458
- $result = $this->EventPrice($oldpost, 'Sale price', $oldSalePrice, $newSalePrice);
459
- }
460
- return $result;
461
- }
462
-
463
- /**
464
- * Group the Price changes in one function
465
- */
466
- private function EventPrice($oldpost, $type, $oldPrice, $newPrice)
467
- {
468
- $currency = $this->GetCurrencySymbol($this->GetConfig('currency'));
469
- $editorLink = $this->GetEditorLink($oldpost);
470
- $this->plugin->alerts->Trigger(9016, array(
471
- 'ProductTitle' => $oldpost->post_title,
472
- 'PriceType' => $type,
473
- 'OldPrice' => (!empty($oldPrice) ? $currency.$oldPrice : 0),
474
- 'NewPrice' => $currency.$newPrice,
475
- $editorLink['name'] => $editorLink['value']
476
- ));
477
- return 1;
478
- }
479
-
480
- /**
481
- * Trigger events 9017
482
- */
483
- protected function CheckSKUChange($oldpost)
484
- {
485
- $oldSku = get_post_meta($oldpost->ID, '_sku', true);
486
- $newSku = isset($_POST['_sku']) ? $_POST['_sku'] : null;
487
-
488
- if (($newSku) && ($oldSku != $newSku)) {
489
- $editorLink = $this->GetEditorLink($oldpost);
490
- $this->plugin->alerts->Trigger(9017, array(
491
- 'ProductTitle' => $oldpost->post_title,
492
- 'OldSku' => (!empty($oldSku) ? $oldSku : 0),
493
- 'NewSku' => $newSku,
494
- $editorLink['name'] => $editorLink['value']
495
- ));
496
- return 1;
497
- }
498
- return 0;
499
- }
500
-
501
- /**
502
- * Trigger events 9018
503
- */
504
- protected function CheckStockStatusChange($oldpost)
505
- {
506
- $oldStatus = $this->_OldStockStatus;
507
- $newStatus = isset($_POST['_stock_status']) ? $_POST['_stock_status'] : null;
508
-
509
- if (($oldStatus && $newStatus) && ($oldStatus != $newStatus)) {
510
- $editorLink = $this->GetEditorLink($oldpost);
511
- $this->plugin->alerts->Trigger(9018, array(
512
- 'ProductTitle' => $oldpost->post_title,
513
- 'OldStatus' => $this->GetStockStatusName($oldStatus),
514
- 'NewStatus' => $this->GetStockStatusName($newStatus),
515
- $editorLink['name'] => $editorLink['value']
516
- ));
517
- return 1;
518
- }
519
- return 0;
520
- }
521
-
522
- /**
523
- * Trigger events 9019
524
- */
525
- protected function CheckStockQuantityChange($oldpost)
526
- {
527
- $oldValue = get_post_meta($oldpost->ID, '_stock', true);
528
- $newValue = isset($_POST['_stock']) ? $_POST['_stock'] : null;
529
-
530
- if (($newValue) && ($oldValue != $newValue)) {
531
- $editorLink = $this->GetEditorLink($oldpost);
532
- $this->plugin->alerts->Trigger(9019, array(
533
- 'ProductTitle' => $oldpost->post_title,
534
- 'OldValue' => (!empty($oldValue) ? $oldValue : 0),
535
- 'NewValue' => $newValue,
536
- $editorLink['name'] => $editorLink['value']
537
- ));
538
- return 1;
539
- }
540
- return 0;
541
- }
542
-
543
- /**
544
- * Trigger events 9020
545
- */
546
- protected function CheckTypeChange($oldpost, $newpost)
547
- {
548
- $result = 0;
549
- if ($oldpost->post_status != 'trash' && $newpost->post_status != 'trash') {
550
- $oldVirtual = get_post_meta($oldpost->ID, '_virtual', true);
551
- $newVirtual = isset($_POST['_virtual']) ? 'yes' : 'no';
552
- $oldDownloadable = get_post_meta($oldpost->ID, '_downloadable', true);
553
- $newDownloadable = isset($_POST['_downloadable']) ? 'yes' : 'no';
554
-
555
- if (($oldVirtual && $newVirtual) && ($oldVirtual != $newVirtual)) {
556
- $type = ($newVirtual == 'no') ? 'Non Virtual' : 'Virtual';
557
- $result = $this->EventType($oldpost, $type);
558
- }
559
- if (($oldDownloadable && $newDownloadable) && ($oldDownloadable != $newDownloadable)) {
560
- $type = ($newDownloadable == 'no') ? 'Non Downloadable' : 'Downloadable';
561
- $result = $this->EventType($oldpost, $type);
562
- }
563
- }
564
- return $result;
565
- }
566
-
567
- /**
568
- * Group the Type changes in one function
569
- */
570
- private function EventType($oldpost, $type)
571
- {
572
- $editorLink = $this->GetEditorLink($oldpost);
573
- $this->plugin->alerts->Trigger(9020, array(
574
- 'ProductTitle' => $oldpost->post_title,
575
- 'Type' => $type,
576
- $editorLink['name'] => $editorLink['value']
577
- ));
578
- return 1;
579
- }
580
-
581
- /**
582
- * Trigger events 9021
583
- */
584
- protected function CheckWeightChange($oldpost)
585
- {
586
- $oldWeight = get_post_meta($oldpost->ID, '_weight', true);
587
- $newWeight = isset($_POST['_weight']) ? $_POST['_weight'] : null;
588
-
589
- if (($newWeight) && ($oldWeight != $newWeight)) {
590
- $editorLink = $this->GetEditorLink($oldpost);
591
- $this->plugin->alerts->Trigger(9021, array(
592
- 'ProductTitle' => $oldpost->post_title,
593
- 'OldWeight' => (!empty($oldWeight) ? $oldWeight : 0),
594
- 'NewWeight' => $newWeight,
595
- $editorLink['name'] => $editorLink['value']
596
- ));
597
- return 1;
598
- }
599
- return 0;
600
- }
601
-
602
- /**
603
- * Trigger events 9022
604
- */
605
- protected function CheckDimensionsChange($oldpost)
606
- {
607
- $result = 0;
608
- $oldLength = get_post_meta($oldpost->ID, '_length', true);
609
- $newLength = isset($_POST['_length']) ? $_POST['_length'] : null;
610
- $oldWidth = get_post_meta($oldpost->ID, '_width', true);
611
- $newWidth = isset($_POST['_width']) ? $_POST['_width'] : null;
612
- $oldHeight = get_post_meta($oldpost->ID, '_height', true);
613
- $newHeight = isset($_POST['_height']) ? $_POST['_height'] : null;
614
-
615
- if (($newLength) && ($oldLength != $newLength)) {
616
- $result = $this->EventDimension($oldpost, 'Length', $oldLength, $newLength);
617
- }
618
- if (($newWidth) && ($oldWidth != $newWidth)) {
619
- $result = $this->EventDimension($oldpost, 'Width', $oldWidth, $newWidth);
620
- }
621
- if (($newHeight) && ($oldHeight != $newHeight)) {
622
- $result = $this->EventDimension($oldpost, 'Height', $oldHeight, $newHeight);
623
- }
624
- return $result;
625
- }
626
-
627
- /**
628
- * Group the Dimension changes in one function
629
- */
630
- private function EventDimension($oldpost, $type, $oldDimension, $newDimension)
631
- {
632
- $dimension_unit = $this->GetConfig('dimension_unit');
633
- $editorLink = $this->GetEditorLink($oldpost);
634
- $this->plugin->alerts->Trigger(9022, array(
635
- 'ProductTitle' => $oldpost->post_title,
636
- 'DimensionType' => $type,
637
- 'OldDimension' => (!empty($oldDimension) ? $dimension_unit.' '.$oldDimension : 0),
638
- 'NewDimension' => $dimension_unit.' '.$newDimension,
639
- $editorLink['name'] => $editorLink['value']
640
- ));
641
- return 1;
642
- }
643
-
644
- /**
645
- * Trigger events 9023, 9024, 9025, 9026
646
- */
647
- protected function CheckDownloadableFileChange($oldpost)
648
- {
649
- $result = 0;
650
- $isUrlChanged = false;
651
- $isNameChanged = false;
652
- $newFileNames = !empty($_POST['_wc_file_names']) ? $_POST['_wc_file_names'] : array();
653
- $newFileUrls = !empty($_POST['_wc_file_urls']) ? $_POST['_wc_file_urls'] : array();
654
- $editorLink = $this->GetEditorLink($oldpost);
655
-
656
- $addedUrls = array_diff($newFileUrls, $this->_OldFileUrls);
657
- // Added files to the product
658
- if (count($addedUrls) > 0) {
659
- // if the file has only changed URL
660
- if (count($newFileUrls) == count($this->_OldFileUrls)) {
661
- $isUrlChanged = true;
662
- } else {
663
- foreach ($addedUrls as $key => $url) {
664
- $this->plugin->alerts->Trigger(9023, array(
665
- 'ProductTitle' => $oldpost->post_title,
666
- 'FileName' => $newFileNames[$key],
667
- 'FileUrl' => $url,
668
- $editorLink['name'] => $editorLink['value']
669
- ));
670
- }
671
- $result = 1;
672
- }
673
- }
674
-
675
- $removedUrls = array_diff($this->_OldFileUrls, $newFileUrls);
676
- // Removed files from the product
677
- if (count($removedUrls) > 0) {
678
- // if the file has only changed URL
679
- if (count($newFileUrls) == count($this->_OldFileUrls)) {
680
- $isUrlChanged = true;
681
- } else {
682
- foreach ($removedUrls as $key => $url) {
683
- $this->plugin->alerts->Trigger(9024, array(
684
- 'ProductTitle' => $oldpost->post_title,
685
- 'FileName' => $this->_OldFileNames[$key],
686
- 'FileUrl' => $url,
687
- $editorLink['name'] => $editorLink['value']
688
- ));
689
- }
690
- $result = 1;
691
- }
692
- }
693
-
694
- $addedNames = array_diff($newFileNames, $this->_OldFileNames);
695
- if (count($addedNames) > 0) {
696
- // if the file has only changed Name
697
- if (count($newFileNames) == count($this->_OldFileNames)) {
698
- foreach ($addedNames as $key => $name) {
699
- $this->plugin->alerts->Trigger(9025, array(
700
- 'ProductTitle' => $oldpost->post_title,
701
- 'OldName' => $this->_OldFileNames[$key],
702
- 'NewName' => $name,
703
- $editorLink['name'] => $editorLink['value']
704
- ));
705
- }
706
- $result = 1;
707
- }
708
- }
709
-
710
- if ($isUrlChanged) {
711
- foreach ($addedUrls as $key => $url) {
712
- $this->plugin->alerts->Trigger(9026, array(
713
- 'ProductTitle' => $oldpost->post_title,
714
- 'FileName' => $newFileNames[$key],
715
- 'OldUrl' => $removedUrls[$key],
716
- 'NewUrl' => $url,
717
- $editorLink['name'] => $editorLink['value']
718
- ));
719
- }
720
- $result = 1;
721
- }
722
- return $result;
723
- }
724
-
725
- /**
726
- * Trigger events Settings: 9027, 9028, 9029, 9030, 9031, 9032, 9033
727
- */
728
- protected function CheckSettingsChange()
729
- {
730
- if (isset($_GET['page']) && $_GET['page'] == 'wc-settings') {
731
- if (isset($_GET['tab']) && $_GET['tab'] == 'products') {
732
- if (isset($_POST['woocommerce_weight_unit'])) {
733
- $oldUnit = $this->GetConfig('weight_unit');
734
- $newUnit = $_POST['woocommerce_weight_unit'];
735
- if ($oldUnit != $newUnit) {
736
- $this->plugin->alerts->Trigger(9027, array(
737
- 'OldUnit' => $oldUnit,
738
- 'NewUnit' => $newUnit
739
- ));
740
- }
741
- }
742
- if (isset($_POST['woocommerce_dimension_unit'])) {
743
- $oldUnit = $this->GetConfig('dimension_unit');
744
- $newUnit = $_POST['woocommerce_dimension_unit'];
745
- if ($oldUnit != $newUnit) {
746
- $this->plugin->alerts->Trigger(9028, array(
747
- 'OldUnit' => $oldUnit,
748
- 'NewUnit' => $newUnit
749
- ));
750
- }
751
- }
752
- } else if (isset($_GET['tab']) && $_GET['tab'] == 'checkout') {
753
- if (!empty($_POST)) {
754
- $oldEnableCoupons = $this->GetConfig('enable_coupons');
755
- $newEnableCoupons = isset($_POST['woocommerce_enable_coupons']) ? 'yes' : 'no';
756
- if ($oldEnableCoupons != $newEnableCoupons) {
757
- $status = ($newEnableCoupons == 'yes') ? 'Enabled' : 'Disabled';
758
- $this->plugin->alerts->Trigger(9032, array(
759
- 'Status' => $status,
760
- ));
761
- }
762
- $oldEnableGuestCheckout = $this->GetConfig('enable_guest_checkout');
763
- $newEnableGuestCheckout = isset($_POST['woocommerce_enable_guest_checkout']) ? 'yes' : 'no';
764
- if ($oldEnableGuestCheckout != $newEnableGuestCheckout) {
765
- $status = ($newEnableGuestCheckout == 'yes') ? 'Enabled' : 'Disabled';
766
- $this->plugin->alerts->Trigger(9033, array(
767
- 'Status' => $status,
768
- ));
769
- }
770
- }
771
- } else {
772
- if (isset($_POST['woocommerce_default_country'])) {
773
- $oldLocation = $this->GetConfig('default_country');
774
- $newLocation = $_POST['woocommerce_default_country'];
775
- if ($oldLocation != $newLocation) {
776
- $this->plugin->alerts->Trigger(9029, array(
777
- 'OldLocation' => $oldLocation,
778
- 'NewLocation' => $newLocation
779
- ));
780
- }
781
- $oldCalcTaxes = $this->GetConfig('calc_taxes');
782
- $newCalcTaxes = isset($_POST['woocommerce_calc_taxes']) ? 'yes' : 'no';
783
- if ($oldCalcTaxes != $newCalcTaxes) {
784
- $status = ($newCalcTaxes == 'yes') ? 'Enabled' : 'Disabled';
785
- $this->plugin->alerts->Trigger(9030, array(
786
- 'Status' => $status,
787
- ));
788
- }
789
- }
790
- if (isset($_POST['woocommerce_currency'])) {
791
- $oldCurrency = $this->GetConfig('currency');
792
- $newCurrency = $_POST['woocommerce_currency'];
793
- if ($oldCurrency != $newCurrency) {
794
- $this->plugin->alerts->Trigger(9031, array(
795
- 'OldCurrency' => $oldCurrency,
796
- 'NewCurrency' => $newCurrency
797
- ));
798
- }
799
- }
800
- }
801
- }
802
- }
803
-
804
- private function GetStockStatusName($slug)
805
- {
806
- if ($slug == 'instock') {
807
- return __('In stock', 'wp-security-audit-log');
808
- } else if ($slug == 'outofstock') {
809
- return __('Out of stock', 'wp-security-audit-log');
810
- }
811
- }
812
-
813
- protected function GetProductCategories($post)
814
- {
815
- return wp_get_post_terms($post->ID, 'product_cat', array("fields" => "names"));
816
- }
817
-
818
- protected function GetProductData($post)
819
- {
820
- return wp_get_post_terms($post->ID, 'product_type', array("fields" => "names"));
821
- }
822
-
823
- /**
824
- * Get the config setting
825
- * @param string $option_name
826
- */
827
- private function GetConfig($option_name)
828
- {
829
- $fn = $this->IsMultisite() ? 'get_site_option' : 'get_option';
830
- return $fn('woocommerce_' . $option_name);
831
- }
832
-
833
- /**
834
- * Check post type.
835
- * @param stdClass $post post
836
- */
837
- private function CheckWooCommerce($post)
838
- {
839
- switch ($post->post_type) {
840
- case 'product':
841
- return true;
842
- default:
843
- return false;
844
- }
845
- }
846
-
847
- /**
848
- * Get editor link.
849
- * @param stdClass $post the post
850
- * @return array $aLink name and value link
851
- */
852
- private function GetEditorLink($post)
853
- {
854
- $name = 'EditorLinkProduct';
855
- $value = get_edit_post_link($post->ID);
856
- $aLink = array(
857
- 'name' => $name,
858
- 'value' => $value,
859
- );
860
- return $aLink;
861
- }
862
-
863
- /**
864
- * Get Currency symbol.
865
- * @param string $currency (default: '')
866
- * @return string
867
- */
868
- private function GetCurrencySymbol($currency = '')
869
- {
870
- $symbols = array(
871
- 'AED' => '&#x62f;.&#x625;','AFN' => '&#x60b;','ALL' => 'L','AMD' => 'AMD','ANG' => '&fnof;','AOA' => 'Kz','ARS' => '&#36;',
872
- 'AUD' => '&#36;','AWG' => '&fnof;','AZN' => 'AZN','BAM' => 'KM','BBD' => '&#36;','BDT' => '&#2547;&nbsp;','BGN' => '&#1083;&#1074;.',
873
- 'BHD' => '.&#x62f;.&#x628;','BIF' => 'Fr','BMD' => '&#36;','BND' => '&#36;','BOB' => 'Bs.','BRL' => '&#82;&#36;','BSD' => '&#36;',
874
- 'BTC' => '&#3647;','BTN' => 'Nu.','BWP' => 'P','BYR' => 'Br','BZD' => '&#36;','CAD' => '&#36;','CDF' => 'Fr','CHF' => '&#67;&#72;&#70;',
875
- 'CLP' => '&#36;','CNY' => '&yen;','COP' => '&#36;','CRC' => '&#x20a1;','CUC' => '&#36;','CUP' => '&#36;','CVE' => '&#36;',
876
- 'CZK' => '&#75;&#269;','DJF' => 'Fr','DKK' => 'DKK','DOP' => 'RD&#36;','DZD' => '&#x62f;.&#x62c;','EGP' => 'EGP','ERN' => 'Nfk',
877
- 'ETB' => 'Br','EUR' => '&euro;','FJD' => '&#36;','FKP' => '&pound;','GBP' => '&pound;','GEL' => '&#x10da;','GGP' => '&pound;',
878
- 'GHS' => '&#x20b5;','GIP' => '&pound;','GMD' => 'D','GNF' => 'Fr','GTQ' => 'Q','GYD' => '&#36;','HKD' => '&#36;','HNL' => 'L',
879
- 'HRK' => 'Kn','HTG' => 'G','HUF' => '&#70;&#116;','IDR' => 'Rp','ILS' => '&#8362;','IMP' => '&pound;','INR' => '&#8377;',
880
- 'IQD' => '&#x639;.&#x62f;','IRR' => '&#xfdfc;','ISK' => 'kr.','JEP' => '&pound;','JMD' => '&#36;','JOD' => '&#x62f;.&#x627;',
881
- 'JPY' => '&yen;','KES' => 'KSh','KGS' => '&#x441;&#x43e;&#x43c;','KHR' => '&#x17db;','KMF' => 'Fr','KPW' => '&#x20a9;','KRW' => '&#8361;',
882
- 'KWD' => '&#x62f;.&#x643;','KYD' => '&#36;','KZT' => 'KZT','LAK' => '&#8365;','LBP' => '&#x644;.&#x644;','LKR' => '&#xdbb;&#xdd4;',
883
- 'LRD' => '&#36;','LSL' => 'L','LYD' => '&#x644;.&#x62f;','MAD' => '&#x62f;. &#x645;.','MAD' => '&#x62f;.&#x645;.','MDL' => 'L','MGA' => 'Ar',
884
- 'MKD' => '&#x434;&#x435;&#x43d;','MMK' => 'Ks','MNT' => '&#x20ae;','MOP' => 'P','MRO' => 'UM','MUR' => '&#x20a8;','MVR' => '.&#x783;',
885
- 'MWK' => 'MK','MXN' => '&#36;','MYR' => '&#82;&#77;','MZN' => 'MT','NAD' => '&#36;','NGN' => '&#8358;','NIO' => 'C&#36;',
886
- 'NOK' => '&#107;&#114;','NPR' => '&#8360;','NZD' => '&#36;','OMR' => '&#x631;.&#x639;.','PAB' => 'B/.','PEN' => 'S/.',
887
- 'PGK' => 'K','PHP' => '&#8369;','PKR' => '&#8360;','PLN' => '&#122;&#322;','PRB' => '&#x440;.','PYG' => '&#8370;','QAR' => '&#x631;.&#x642;',
888
- 'RMB' => '&yen;','RON' => 'lei','RSD' => '&#x434;&#x438;&#x43d;.','RUB' => '&#8381;','RWF' => 'Fr','SAR' => '&#x631;.&#x633;',
889
- 'SBD' => '&#36;','SCR' => '&#x20a8;','SDG' => '&#x62c;.&#x633;.','SEK' => '&#107;&#114;','SGD' => '&#36;','SHP' => '&pound;','SLL' => 'Le',
890
- 'SOS' => 'Sh','SRD' => '&#36;','SSP' => '&pound;','STD' => 'Db','SYP' => '&#x644;.&#x633;','SZL' => 'L','THB' => '&#3647;',
891
- 'TJS' => '&#x405;&#x41c;','TMT' => 'm','TND' => '&#x62f;.&#x62a;','TOP' => 'T&#36;','TRY' => '&#8378;','TTD' => '&#36;',
892
- 'TWD' => '&#78;&#84;&#36;','TZS' => 'Sh','UAH' => '&#8372;','UGX' => 'UGX','USD' => '&#36;','UYU' => '&#36;','UZS' => 'UZS',
893
- 'VEF' => 'Bs F','VND' => '&#8363;','VUV' => 'Vt','WST' => 'T','XAF' => 'Fr','XCD' => '&#36;','XOF' => 'Fr','XPF' => 'Fr',
894
- 'YER' => '&#xfdfc;','ZAR' => '&#82;','ZMW' => 'ZK',
895
- );
896
- $currency_symbol = isset($symbols[$currency]) ? $symbols[ $currency ] : '';
897
-
898
- return $currency_symbol;
899
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
900
  }
1
  <?php
2
  /**
3
+ * Sensor: WooCommerce
4
+ *
5
+ * WooCommerce sensor file.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * Support for WooCommerce Plugin.
17
  *
18
  * 9000 User created a new product
49
  * 9031 User changed the currency
50
  * 9032 User Enabled/Disabled the use of coupons during checkout
51
  * 9033 User Enabled/Disabled guest checkout
52
+ *
53
+ * @package Wsal
54
+ * @subpackage Sensors
55
  */
56
+ class WSAL_Sensors_WooCommerce extends WSAL_AbstractSensor {
57
+
58
+ /**
59
+ * Old Post.
60
+ *
61
+ * @var WP_Post
62
+ */
63
+ protected $_old_post = null;
64
+
65
+ /**
66
+ * Old Post Link.
67
+ *
68
+ * @var string
69
+ */
70
+ protected $_old_link = null;
71
+
72
+ /**
73
+ * Old Post Categories.
74
+ *
75
+ * @var array
76
+ */
77
+ protected $_old_cats = null;
78
+
79
+ /**
80
+ * Old Product Data.
81
+ *
82
+ * @var array
83
+ */
84
+ protected $_old_data = null;
85
+
86
+ /**
87
+ * Old Product Stock Status.
88
+ *
89
+ * @var string
90
+ */
91
+ protected $_old_stock_status = null;
92
+
93
+ /**
94
+ * Old Product File Names.
95
+ *
96
+ * @var array
97
+ */
98
+ protected $_old_file_names = array();
99
+
100
+ /**
101
+ * Old Product File URLs.
102
+ *
103
+ * @var array
104
+ */
105
+ protected $_old_file_urls = array();
106
+
107
+ /**
108
+ * Listening to events using WP hooks.
109
+ */
110
+ public function HookEvents() {
111
+ if ( current_user_can( 'edit_posts' ) ) {
112
+ add_action( 'admin_init', array( $this, 'EventAdminInit' ) );
113
+ }
114
+ add_action( 'post_updated', array( $this, 'EventChanged' ), 10, 3 );
115
+ add_action( 'delete_post', array( $this, 'EventDeleted' ), 10, 1 );
116
+ add_action( 'wp_trash_post', array( $this, 'EventTrashed' ), 10, 1 );
117
+ add_action( 'untrash_post', array( $this, 'EventUntrashed' ) );
118
+
119
+ add_action( 'create_product_cat', array( $this, 'EventCategoryCreation' ), 10, 1 );
120
+ /* add_action('edit_product_cat', array($this, 'EventCategoryChanged'), 10, 1); */
121
+ }
122
+
123
+ /**
124
+ * Triggered when a user accesses the admin area.
125
+ */
126
+ public function EventAdminInit() {
127
+ // Load old data, if applicable.
128
+ $this->RetrieveOldData();
129
+ $this->CheckSettingsChange();
130
+ }
131
+
132
+ /**
133
+ * Retrieve Old data.
134
+ *
135
+ * @global mixed $_POST post data
136
+ */
137
+ protected function RetrieveOldData() {
138
+ // Filter POST global array.
139
+ $post_array = filter_input_array( INPUT_POST );
140
+
141
+ if ( isset( $post_array['post_ID'] )
142
+ && isset( $post_array['_wpnonce'] )
143
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
144
+ return false;
145
+ }
146
+
147
+ if ( isset( $post_array ) && isset( $post_array['post_ID'] )
148
+ && ! (defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE)
149
+ && ! (isset( $post_array['action'] ) && 'autosave' == $post_array['action'] )
150
+ ) {
151
+ $post_id = intval( $post_array['post_ID'] );
152
+ $this->_old_post = get_post( $post_id );
153
+ $this->_old_link = get_post_permalink( $post_id, false, true );
154
+ $this->_old_cats = $this->GetProductCategories( $this->_old_post );
155
+ $this->_old_data = $this->GetProductData( $this->_old_post );
156
+ $this->_old_stock_status = get_post_meta( $post_id, '_stock_status', true );
157
+
158
+ $old_downloadable_files = get_post_meta( $post_id, '_downloadable_files', true );
159
+ if ( ! empty( $old_downloadable_files ) ) {
160
+ foreach ( $old_downloadable_files as $file ) {
161
+ array_push( $this->_old_file_names, $file['name'] );
162
+ array_push( $this->_old_file_urls, $file['file'] );
163
+ }
164
+ }
165
+ }
166
+ }
167
+
168
+ /**
169
+ * WooCommerce Product Updated.
170
+ *
171
+ * @param integer $post_id - Post ID.
172
+ * @param stdClass $newpost - The new post.
173
+ * @param stdClass $oldpost - The old post.
174
+ */
175
+ public function EventChanged( $post_id, $newpost, $oldpost ) {
176
+ if ( $this->CheckWooCommerce( $oldpost ) ) {
177
+ $changes = 0 + $this->EventCreation( $oldpost, $newpost );
178
+ if ( ! $changes ) {
179
+ // Change Categories.
180
+ $changes = $this->CheckCategoriesChange( $this->_old_cats, $this->GetProductCategories( $newpost ), $oldpost, $newpost );
181
+ }
182
+ if ( ! $changes ) {
183
+ // Change Short description, Text, URL, Product Data, Date, Visibility, etc.
184
+ $changes = 0
185
+ + $this->CheckShortDescriptionChange( $oldpost, $newpost )
186
+ + $this->CheckTextChange( $oldpost, $newpost )
187
+ + $this->CheckProductDataChange( $this->_old_data, $newpost )
188
+ + $this->CheckDateChange( $oldpost, $newpost )
189
+ + $this->CheckVisibilityChange( $oldpost )
190
+ + $this->CheckStatusChange( $oldpost, $newpost )
191
+ + $this->CheckPriceChange( $oldpost )
192
+ + $this->CheckSKUChange( $oldpost )
193
+ + $this->CheckStockStatusChange( $oldpost )
194
+ + $this->CheckStockQuantityChange( $oldpost )
195
+ + $this->CheckTypeChange( $oldpost, $newpost )
196
+ + $this->CheckWeightChange( $oldpost )
197
+ + $this->CheckDimensionsChange( $oldpost )
198
+ + $this->CheckDownloadableFileChange( $oldpost );
199
+ }
200
+ if ( ! $changes ) {
201
+ // Change Permalink.
202
+ $changes = $this->CheckPermalinkChange( $this->_old_link, get_post_permalink( $post_id, false, true ), $newpost );
203
+ if ( ! $changes ) {
204
+ // If no one of the above changes happen.
205
+ $this->CheckModifyChange( $oldpost, $newpost );
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * WooCommerce Product Created.
213
+ *
214
+ * Trigger events 9000, 9001.
215
+ *
216
+ * @param object $old_post - Old Product.
217
+ * @param object $new_post - New Product.
218
+ */
219
+ private function EventCreation( $old_post, $new_post ) {
220
+ // Filter POST global array.
221
+ $post_array = filter_input_array( INPUT_POST );
222
+
223
+ if ( isset( $post_array['post_ID'] )
224
+ && isset( $post_array['_wpnonce'] )
225
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
226
+ return false;
227
+ }
228
+
229
+ $original = isset( $post_array['original_post_status'] ) ? $post_array['original_post_status'] : '';
230
+ if ( 'draft' == $original && 'draft' == $new_post->post_status ) {
231
+ return 0;
232
+ }
233
+
234
+ if ( 'draft' == $old_post->post_status || 'auto-draft' == $original ) {
235
+ if ( 'product' == $old_post->post_type ) {
236
+ $editor_link = $this->GetEditorLink( $new_post );
237
+ if ( 'draft' == $new_post->post_status ) {
238
+ $this->plugin->alerts->Trigger(
239
+ 9000, array(
240
+ 'ProductTitle' => $new_post->post_title,
241
+ $editor_link['name'] => $editor_link['value'],
242
+ )
243
+ );
244
+ return 1;
245
+ } elseif ( 'publish' == $new_post->post_status ) {
246
+ $this->plugin->alerts->Trigger(
247
+ 9001, array(
248
+ 'ProductTitle' => $new_post->post_title,
249
+ 'ProductUrl' => get_post_permalink( $new_post->ID ),
250
+ $editor_link['name'] => $editor_link['value'],
251
+ )
252
+ );
253
+ return 1;
254
+ }
255
+ }
256
+ }
257
+ return 0;
258
+ }
259
+
260
+ /**
261
+ * Trigger events 9002
262
+ *
263
+ * @param int|WP_Term $term_id - Term ID.
264
+ */
265
+ public function EventCategoryCreation( $term_id = null ) {
266
+ $term = get_term( $term_id );
267
+ if ( ! empty( $term ) ) {
268
+ $this->plugin->alerts->Trigger(
269
+ 9002, array(
270
+ 'CategoryName' => $term->name,
271
+ 'Slug' => $term->slug,
272
+ )
273
+ );
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Not implemented
279
+ *
280
+ * @param int|WP_Term $term_id - Term ID.
281
+ */
282
+ /**
283
+ public function EventCategoryChanged( $term_id = null ) {
284
+ $old_term = get_term( $term_id );
285
+ if ( isset( $_POST['taxonomy'] ) ) {
286
+ // new $term in $_POST
287
+ }
288
+ } */
289
+
290
+ /**
291
+ * Trigger events 9003
292
+ *
293
+ * @param array $old_cats - Old Categories.
294
+ * @param array $new_cats - New Categories.
295
+ * @param object $oldpost - Old product object.
296
+ * @param object $newpost - New product object.
297
+ * @return int
298
+ */
299
+ protected function CheckCategoriesChange( $old_cats, $new_cats, $oldpost, $newpost ) {
300
+ if ( 'trash' == $newpost->post_status || 'trash' == $oldpost->post_status ) {
301
+ return 0;
302
+ }
303
+ $old_cats = is_array( $old_cats ) ? implode( ', ', $old_cats ) : $old_cats;
304
+ $new_cats = is_array( $new_cats ) ? implode( ', ', $new_cats ) : $new_cats;
305
+ if ( $old_cats != $new_cats ) {
306
+ $editor_link = $this->GetEditorLink( $newpost );
307
+ $this->plugin->alerts->Trigger(
308
+ 9003, array(
309
+ 'ProductTitle' => $newpost->post_title,
310
+ 'OldCategories' => $old_cats ? $old_cats : 'no categories',
311
+ 'NewCategories' => $new_cats ? $new_cats : 'no categories',
312
+ $editor_link['name'] => $editor_link['value'],
313
+ )
314
+ );
315
+ return 1;
316
+ }
317
+ return 0;
318
+ }
319
+
320
+ /**
321
+ * Trigger events 9004
322
+ *
323
+ * @param object $oldpost - Old product object.
324
+ * @param object $newpost - New product object.
325
+ * @return int
326
+ */
327
+ protected function CheckShortDescriptionChange( $oldpost, $newpost ) {
328
+ if ( 'auto-draft' == $oldpost->post_status ) {
329
+ return 0;
330
+ }
331
+ if ( $oldpost->post_excerpt != $newpost->post_excerpt ) {
332
+ $editor_link = $this->GetEditorLink( $oldpost );
333
+ $this->plugin->alerts->Trigger(
334
+ 9004, array(
335
+ 'ProductTitle' => $oldpost->post_title,
336
+ $editor_link['name'] => $editor_link['value'],
337
+ )
338
+ );
339
+ return 1;
340
+ }
341
+ return 0;
342
+ }
343
+
344
+ /**
345
+ * Trigger events 9005
346
+ *
347
+ * @param object $oldpost - Old product object.
348
+ * @param object $newpost - New product object.
349
+ * @return int
350
+ */
351
+ protected function CheckTextChange( $oldpost, $newpost ) {
352
+ if ( 'auto-draft' == $oldpost->post_status ) {
353
+ return 0;
354
+ }
355
+ if ( $oldpost->post_content != $newpost->post_content ) {
356
+ $editor_link = $this->GetEditorLink( $oldpost );
357
+ $this->plugin->alerts->Trigger(
358
+ 9005, array(
359
+ 'ProductTitle' => $oldpost->post_title,
360
+ $editor_link['name'] => $editor_link['value'],
361
+ )
362
+ );
363
+ return 1;
364
+ }
365
+ return 0;
366
+ }
367
+
368
+ /**
369
+ * Trigger events 9006
370
+ *
371
+ * @param string $old_link - Old product link.
372
+ * @param string $new_link - New product link.
373
+ * @param object $post - Product object.
374
+ * @return int
375
+ */
376
+ protected function CheckPermalinkChange( $old_link, $new_link, $post ) {
377
+ if ( ($old_link && $new_link) && ($old_link != $new_link) ) {
378
+ $editor_link = $this->GetEditorLink( $post );
379
+ $this->plugin->alerts->Trigger(
380
+ 9006, array(
381
+ 'ProductTitle' => $post->post_title,
382
+ 'OldUrl' => $old_link,
383
+ 'NewUrl' => $new_link,
384
+ $editor_link['name'] => $editor_link['value'],
385
+ )
386
+ );
387
+ return 1;
388
+ }
389
+ return 0;
390
+ }
391
+
392
+ /**
393
+ * Trigger events 9007
394
+ *
395
+ * @param array $old_data - Product Data.
396
+ * @param object $post - Product object.
397
+ * @return int
398
+ */
399
+ protected function CheckProductDataChange( $old_data, $post ) {
400
+ // Filter POST global array.
401
+ $post_array = filter_input_array( INPUT_POST );
402
+
403
+ if ( isset( $post_array['post_ID'] )
404
+ && isset( $post_array['_wpnonce'] )
405
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
406
+ return false;
407
+ }
408
+
409
+ if ( isset( $post_array['product-type'] ) ) {
410
+ $old_data = is_array( $old_data ) ? implode( ', ', $old_data ) : $old_data;
411
+ $new_data = $post_array['product-type'];
412
+ if ( $old_data != $new_data ) {
413
+ $editor_link = $this->GetEditorLink( $post );
414
+ $this->plugin->alerts->Trigger(
415
+ 9007, array(
416
+ 'ProductTitle' => $post->post_title,
417
+ $editor_link['name'] => $editor_link['value'],
418
+ )
419
+ );
420
+ return 1;
421
+ }
422
+ }
423
+ return 0;
424
+ }
425
+
426
+ /**
427
+ * Trigger events 9008
428
+ *
429
+ * @param object $oldpost - Old product object.
430
+ * @param object $newpost - New product object.
431
+ * @return int
432
+ */
433
+ protected function CheckDateChange( $oldpost, $newpost ) {
434
+ if ( 'draft' == $oldpost->post_status || 'auto-draft' == $oldpost->post_status ) {
435
+ return 0;
436
+ }
437
+ $from = strtotime( $oldpost->post_date );
438
+ $to = strtotime( $newpost->post_date );
439
+ if ( $from != $to ) {
440
+ $editor_link = $this->GetEditorLink( $oldpost );
441
+ $this->plugin->alerts->Trigger(
442
+ 9008, array(
443
+ 'ProductTitle' => $oldpost->post_title,
444
+ 'OldDate' => $oldpost->post_date,
445
+ 'NewDate' => $newpost->post_date,
446
+ $editor_link['name'] => $editor_link['value'],
447
+ )
448
+ );
449
+ return 1;
450
+ }
451
+ return 0;
452
+ }
453
+
454
+ /**
455
+ * Trigger events 9009
456
+ *
457
+ * @param object $oldpost - Old product object.
458
+ * @return int
459
+ */
460
+ protected function CheckVisibilityChange( $oldpost ) {
461
+ // Filter POST global array.
462
+ $post_array = filter_input_array( INPUT_POST );
463
+
464
+ if ( isset( $post_array['post_ID'] )
465
+ && isset( $post_array['_wpnonce'] )
466
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
467
+ return false;
468
+ }
469
+
470
+ $old_visibility = isset( $post_array['hidden_post_visibility'] ) ? $post_array['hidden_post_visibility'] : null;
471
+ $new_visibility = isset( $post_array['visibility'] ) ? $post_array['visibility'] : null;
472
+
473
+ if ( 'password' == $old_visibility ) {
474
+ $old_visibility = __( 'Password Protected', 'wp-security-audit-log' );
475
+ } else {
476
+ $old_visibility = ucfirst( $old_visibility );
477
+ }
478
+
479
+ if ( 'password' == $new_visibility ) {
480
+ $new_visibility = __( 'Password Protected', 'wp-security-audit-log' );
481
+ } else {
482
+ $new_visibility = ucfirst( $new_visibility );
483
+ }
484
+
485
+ if ( ($old_visibility && $new_visibility) && ($old_visibility != $new_visibility) ) {
486
+ $editor_link = $this->GetEditorLink( $oldpost );
487
+ $this->plugin->alerts->Trigger(
488
+ 9009, array(
489
+ 'ProductTitle' => $oldpost->post_title,
490
+ 'OldVisibility' => $old_visibility,
491
+ 'NewVisibility' => $new_visibility,
492
+ $editor_link['name'] => $editor_link['value'],
493
+ )
494
+ );
495
+ return 1;
496
+ }
497
+ return 0;
498
+ }
499
+
500
+ /**
501
+ * Trigger events 9010, 9011
502
+ *
503
+ * @param object $oldpost - Old product object.
504
+ * @param object $newpost - New product object.
505
+ * @return int
506
+ */
507
+ protected function CheckModifyChange( $oldpost, $newpost ) {
508
+ if ( 'trash' == $newpost->post_status ) {
509
+ return 0;
510
+ }
511
+ $editor_link = $this->GetEditorLink( $oldpost );
512
+ if ( 'publish' == $oldpost->post_status ) {
513
+ $this->plugin->alerts->Trigger(
514
+ 9010, array(
515
+ 'ProductTitle' => $oldpost->post_title,
516
+ 'ProductUrl' => get_post_permalink( $oldpost->ID ),
517
+ $editor_link['name'] => $editor_link['value'],
518
+ )
519
+ );
520
+ } elseif ( 'draft' == $oldpost->post_status ) {
521
+ $this->plugin->alerts->Trigger(
522
+ 9011, array(
523
+ 'ProductTitle' => $oldpost->post_title,
524
+ $editor_link['name'] => $editor_link['value'],
525
+ )
526
+ );
527
+ }
528
+ }
529
+
530
+ /**
531
+ * Moved to Trash 9012
532
+ *
533
+ * @param int $post_id - Product ID.
534
+ */
535
+ public function EventTrashed( $post_id ) {
536
+ $post = get_post( $post_id );
537
+ if ( $this->CheckWooCommerce( $post ) ) {
538
+ $this->plugin->alerts->Trigger(
539
+ 9012, array(
540
+ 'ProductTitle' => $post->post_title,
541
+ 'ProductUrl' => get_post_permalink( $post->ID ),
542
+ )
543
+ );
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Permanently deleted 9013
549
+ *
550
+ * @param int $post_id - Product ID.
551
+ */
552
+ public function EventDeleted( $post_id ) {
553
+ $post = get_post( $post_id );
554
+ if ( $this->CheckWooCommerce( $post ) ) {
555
+ $this->plugin->alerts->Trigger(
556
+ 9013, array(
557
+ 'ProductTitle' => $post->post_title,
558
+ )
559
+ );
560
+ }
561
+ }
562
+
563
+ /**
564
+ * Restored from Trash 9014
565
+ *
566
+ * @param int $post_id - Product ID.
567
+ */
568
+ public function EventUntrashed( $post_id ) {
569
+ $post = get_post( $post_id );
570
+ if ( $this->CheckWooCommerce( $post ) ) {
571
+ $editor_link = $this->GetEditorLink( $post );
572
+ $this->plugin->alerts->Trigger(
573
+ 9014, array(
574
+ 'ProductTitle' => $post->post_title,
575
+ $editor_link['name'] => $editor_link['value'],
576
+ )
577
+ );
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Trigger events 9015
583
+ *
584
+ * @param object $oldpost - Old product object.
585
+ * @param object $newpost - New product object.
586
+ * @return int
587
+ */
588
+ protected function CheckStatusChange( $oldpost, $newpost ) {
589
+ if ( 'draft' == $oldpost->post_status || 'auto-draft' == $oldpost->post_status ) {
590
+ return 0;
591
+ }
592
+ if ( $oldpost->post_status != $newpost->post_status ) {
593
+ if ( 'trash' != $oldpost->post_status && 'trash' != $newpost->post_status ) {
594
+ $editor_link = $this->GetEditorLink( $oldpost );
595
+ $this->plugin->alerts->Trigger(
596
+ 9015, array(
597
+ 'ProductTitle' => $oldpost->post_title,
598
+ 'OldStatus' => $oldpost->post_status,
599
+ 'NewStatus' => $newpost->post_status,
600
+ $editor_link['name'] => $editor_link['value'],
601
+ )
602
+ );
603
+ return 1;
604
+ }
605
+ }
606
+ return 0;
607
+ }
608
+
609
+ /**
610
+ * Trigger events 9016
611
+ *
612
+ * @param object $oldpost - Old product object.
613
+ * @return int
614
+ */
615
+ protected function CheckPriceChange( $oldpost ) {
616
+ // Filter POST global array.
617
+ $post_array = filter_input_array( INPUT_POST );
618
+
619
+ if ( isset( $post_array['post_ID'] )
620
+ && isset( $post_array['_wpnonce'] )
621
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
622
+ return false;
623
+ }
624
+
625
+ $result = 0;
626
+ $old_price = get_post_meta( $oldpost->ID, '_regular_price', true );
627
+ $old_sale_price = get_post_meta( $oldpost->ID, '_sale_price', true );
628
+ $new_price = isset( $post_array['_regular_price'] ) ? $post_array['_regular_price'] : null;
629
+ $new_sale_price = isset( $post_array['_sale_price'] ) ? $post_array['_sale_price'] : null;
630
+
631
+ if ( ($new_price) && ($old_price != $new_price) ) {
632
+ $result = $this->EventPrice( $oldpost, 'Regular price', $old_price, $new_price );
633
+ }
634
+ if ( ($new_sale_price) && ($old_sale_price != $new_sale_price) ) {
635
+ $result = $this->EventPrice( $oldpost, 'Sale price', $old_sale_price, $new_sale_price );
636
+ }
637
+ return $result;
638
+ }
639
+
640
+ /**
641
+ * Group the Price changes in one function
642
+ *
643
+ * @param object $oldpost - Old Product Object.
644
+ * @param string $type - Price Type.
645
+ * @param int $old_price - Old Product Price.
646
+ * @param int $new_price - New Product Price.
647
+ * @return int
648
+ */
649
+ private function EventPrice( $oldpost, $type, $old_price, $new_price ) {
650
+ $currency = $this->GetCurrencySymbol( $this->GetConfig( 'currency' ) );
651
+ $editor_link = $this->GetEditorLink( $oldpost );
652
+ $this->plugin->alerts->Trigger(
653
+ 9016, array(
654
+ 'ProductTitle' => $oldpost->post_title,
655
+ 'PriceType' => $type,
656
+ 'OldPrice' => ( ! empty( $old_price ) ? $currency . $old_price : 0),
657
+ 'NewPrice' => $currency . $new_price,
658
+ $editor_link['name'] => $editor_link['value'],
659
+ )
660
+ );
661
+ return 1;
662
+ }
663
+
664
+ /**
665
+ * Trigger events 9017
666
+ *
667
+ * @param object $oldpost - Old product object.
668
+ * @return int
669
+ */
670
+ protected function CheckSKUChange( $oldpost ) {
671
+ // Filter POST global array.
672
+ $post_array = filter_input_array( INPUT_POST );
673
+
674
+ if ( isset( $post_array['post_ID'] )
675
+ && isset( $post_array['_wpnonce'] )
676
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
677
+ return false;
678
+ }
679
+
680
+ $old_sku = get_post_meta( $oldpost->ID, '_sku', true );
681
+ $new_sku = isset( $post_array['_sku'] ) ? $post_array['_sku'] : null;
682
+
683
+ if ( ($new_sku) && ($old_sku != $new_sku) ) {
684
+ $editor_link = $this->GetEditorLink( $oldpost );
685
+ $this->plugin->alerts->Trigger(
686
+ 9017, array(
687
+ 'ProductTitle' => $oldpost->post_title,
688
+ 'OldSku' => ( ! empty( $old_sku ) ? $old_sku : 0),
689
+ 'NewSku' => $new_sku,
690
+ $editor_link['name'] => $editor_link['value'],
691
+ )
692
+ );
693
+ return 1;
694
+ }
695
+ return 0;
696
+ }
697
+
698
+ /**
699
+ * Trigger events 9018
700
+ *
701
+ * @param object $oldpost - Old product object.
702
+ * @return int
703
+ */
704
+ protected function CheckStockStatusChange( $oldpost ) {
705
+ // Filter POST global array.
706
+ $post_array = filter_input_array( INPUT_POST );
707
+
708
+ if ( isset( $post_array['post_ID'] )
709
+ && isset( $post_array['_wpnonce'] )
710
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
711
+ return false;
712
+ }
713
+
714
+ $old_status = $this->_old_stock_status;
715
+ $new_status = isset( $post_array['_stock_status'] ) ? $post_array['_stock_status'] : null;
716
+
717
+ if ( ($old_status && $new_status) && ($old_status != $new_status) ) {
718
+ $editor_link = $this->GetEditorLink( $oldpost );
719
+ $this->plugin->alerts->Trigger(
720
+ 9018, array(
721
+ 'ProductTitle' => $oldpost->post_title,
722
+ 'OldStatus' => $this->GetStockStatusName( $old_status ),
723
+ 'NewStatus' => $this->GetStockStatusName( $new_status ),
724
+ $editor_link['name'] => $editor_link['value'],
725
+ )
726
+ );
727
+ return 1;
728
+ }
729
+ return 0;
730
+ }
731
+
732
+ /**
733
+ * Trigger events 9019
734
+ *
735
+ * @param object $oldpost - Old product object.
736
+ * @return int
737
+ */
738
+ protected function CheckStockQuantityChange( $oldpost ) {
739
+ // Filter POST global array.
740
+ $post_array = filter_input_array( INPUT_POST );
741
+
742
+ if ( isset( $post_array['post_ID'] )
743
+ && isset( $post_array['_wpnonce'] )
744
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
745
+ return false;
746
+ }
747
+
748
+ $old_value = get_post_meta( $oldpost->ID, '_stock', true );
749
+ $new_value = isset( $post_array['_stock'] ) ? $post_array['_stock'] : null;
750
+
751
+ if ( ($new_value) && ($old_value != $new_value) ) {
752
+ $editor_link = $this->GetEditorLink( $oldpost );
753
+ $this->plugin->alerts->Trigger(
754
+ 9019, array(
755
+ 'ProductTitle' => $oldpost->post_title,
756
+ 'OldValue' => ( ! empty( $old_value ) ? $old_value : 0),
757
+ 'NewValue' => $new_value,
758
+ $editor_link['name'] => $editor_link['value'],
759
+ )
760
+ );
761
+ return 1;
762
+ }
763
+ return 0;
764
+ }
765
+
766
+ /**
767
+ * Trigger events 9020
768
+ *
769
+ * @param object $oldpost - Old product object.
770
+ * @param object $newpost - New product object.
771
+ * @return int
772
+ */
773
+ protected function CheckTypeChange( $oldpost, $newpost ) {
774
+ // Filter POST global array.
775
+ $post_array = filter_input_array( INPUT_POST );
776
+
777
+ if ( isset( $post_array['post_ID'] )
778
+ && isset( $post_array['_wpnonce'] )
779
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
780
+ return false;
781
+ }
782
+
783
+ $result = 0;
784
+ if ( 'trash' != $oldpost->post_status && 'trash' != $newpost->post_status ) {
785
+ $old_virtual = get_post_meta( $oldpost->ID, '_virtual', true );
786
+ $new_virtual = isset( $post_array['_virtual'] ) ? 'yes' : 'no';
787
+ $old_downloadable = get_post_meta( $oldpost->ID, '_downloadable', true );
788
+ $new_downloadable = isset( $post_array['_downloadable'] ) ? 'yes' : 'no';
789
+
790
+ if ( ($old_virtual && $new_virtual) && ($old_virtual != $new_virtual) ) {
791
+ $type = ( 'no' == $new_virtual ) ? 'Non Virtual' : 'Virtual';
792
+ $result = $this->EventType( $oldpost, $type );
793
+ }
794
+ if ( ($old_downloadable && $new_downloadable) && ($old_downloadable != $new_downloadable) ) {
795
+ $type = ( 'no' == $new_downloadable ) ? 'Non Downloadable' : 'Downloadable';
796
+ $result = $this->EventType( $oldpost, $type );
797
+ }
798
+ }
799
+ return $result;
800
+ }
801
+
802
+ /**
803
+ * Group the Type changes in one function.
804
+ *
805
+ * @param object $oldpost - Old product object.
806
+ * @param string $type - Product Type.
807
+ * @return int
808
+ */
809
+ private function EventType( $oldpost, $type ) {
810
+ $editor_link = $this->GetEditorLink( $oldpost );
811
+ $this->plugin->alerts->Trigger(
812
+ 9020, array(
813
+ 'ProductTitle' => $oldpost->post_title,
814
+ 'Type' => $type,
815
+ $editor_link['name'] => $editor_link['value'],
816
+ )
817
+ );
818
+ return 1;
819
+ }
820
+
821
+ /**
822
+ * Trigger events 9021
823
+ *
824
+ * @param object $oldpost - Old product object.
825
+ * @return int
826
+ */
827
+ protected function CheckWeightChange( $oldpost ) {
828
+ // Filter POST global array.
829
+ $post_array = filter_input_array( INPUT_POST );
830
+
831
+ if ( isset( $post_array['post_ID'] )
832
+ && isset( $post_array['_wpnonce'] )
833
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
834
+ return false;
835
+ }
836
+
837
+ $old_weight = get_post_meta( $oldpost->ID, '_weight', true );
838
+ $new_weight = isset( $post_array['_weight'] ) ? $post_array['_weight'] : null;
839
+
840
+ if ( ($new_weight) && ($old_weight != $new_weight) ) {
841
+ $editor_link = $this->GetEditorLink( $oldpost );
842
+ $this->plugin->alerts->Trigger(
843
+ 9021, array(
844
+ 'ProductTitle' => $oldpost->post_title,
845
+ 'OldWeight' => ( ! empty( $old_weight ) ? $old_weight : 0),
846
+ 'NewWeight' => $new_weight,
847
+ $editor_link['name'] => $editor_link['value'],
848
+ )
849
+ );
850
+ return 1;
851
+ }
852
+ return 0;
853
+ }
854
+
855
+ /**
856
+ * Trigger events 9022
857
+ *
858
+ * @param object $oldpost - Old product object.
859
+ * @return int
860
+ */
861
+ protected function CheckDimensionsChange( $oldpost ) {
862
+ // Filter POST global array.
863
+ $post_array = filter_input_array( INPUT_POST );
864
+
865
+ if ( isset( $post_array['post_ID'] )
866
+ && isset( $post_array['_wpnonce'] )
867
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
868
+ return false;
869
+ }
870
+
871
+ $result = 0;
872
+ $old_length = get_post_meta( $oldpost->ID, '_length', true );
873
+ $new_length = isset( $post_array['_length'] ) ? $post_array['_length'] : null;
874
+ $old_width = get_post_meta( $oldpost->ID, '_width', true );
875
+ $new_width = isset( $post_array['_width'] ) ? $post_array['_width'] : null;
876
+ $old_height = get_post_meta( $oldpost->ID, '_height', true );
877
+ $new_height = isset( $post_array['_height'] ) ? $post_array['_height'] : null;
878
+
879
+ if ( ($new_length) && ($old_length != $new_length) ) {
880
+ $result = $this->EventDimension( $oldpost, 'Length', $old_length, $new_length );
881
+ }
882
+ if ( ($new_width) && ($old_width != $new_width) ) {
883
+ $result = $this->EventDimension( $oldpost, 'Width', $old_width, $new_width );
884
+ }
885
+ if ( ($new_height) && ($old_height != $new_height) ) {
886
+ $result = $this->EventDimension( $oldpost, 'Height', $old_height, $new_height );
887
+ }
888
+ return $result;
889
+ }
890
+
891
+ /**
892
+ * Group the Dimension changes in one function.
893
+ *
894
+ * @param object $oldpost - Old Product object.
895
+ * @param string $type - Dimension type.
896
+ * @param string $old_dimension - Old dimension.
897
+ * @param string $new_dimension - New dimension.
898
+ * @return int
899
+ */
900
+ private function EventDimension( $oldpost, $type, $old_dimension, $new_dimension ) {
901
+ $dimension_unit = $this->GetConfig( 'dimension_unit' );
902
+ $editor_link = $this->GetEditorLink( $oldpost );
903
+ $this->plugin->alerts->Trigger(
904
+ 9022, array(
905
+ 'ProductTitle' => $oldpost->post_title,
906
+ 'DimensionType' => $type,
907
+ 'OldDimension' => ( ! empty( $old_dimension ) ? $dimension_unit . ' ' . $old_dimension : 0),
908
+ 'NewDimension' => $dimension_unit . ' ' . $new_dimension,
909
+ $editor_link['name'] => $editor_link['value'],
910
+ )
911
+ );
912
+ return 1;
913
+ }
914
+
915
+ /**
916
+ * Trigger events 9023, 9024, 9025, 9026
917
+ *
918
+ * @param object $oldpost - Old product object.
919
+ * @return int
920
+ */
921
+ protected function CheckDownloadableFileChange( $oldpost ) {
922
+ // Filter POST global array.
923
+ $post_array = filter_input_array( INPUT_POST );
924
+
925
+ if ( isset( $post_array['post_ID'] )
926
+ && isset( $post_array['_wpnonce'] )
927
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'update-post_' . $post_array['post_ID'] ) ) {
928
+ return false;
929
+ }
930
+
931
+ $result = 0;
932
+ $is_url_changed = false;
933
+ $is_name_changed = false;
934
+ $new_file_names = ! empty( $post_array['_wc_file_names'] ) ? $post_array['_wc_file_names'] : array();
935
+ $new_file_urls = ! empty( $post_array['_wc_file_urls'] ) ? $post_array['_wc_file_urls'] : array();
936
+ $editor_link = $this->GetEditorLink( $oldpost );
937
+ $added_urls = array_diff( $new_file_urls, $this->_old_file_urls );
938
+
939
+ // Added files to the product.
940
+ if ( count( $added_urls ) > 0 ) {
941
+ // If the file has only changed URL.
942
+ if ( count( $new_file_urls ) == count( $this->_old_file_urls ) ) {
943
+ $is_url_changed = true;
944
+ } else {
945
+ foreach ( $added_urls as $key => $url ) {
946
+ $this->plugin->alerts->Trigger(
947
+ 9023, array(
948
+ 'ProductTitle' => $oldpost->post_title,
949
+ 'FileName' => $new_file_names[ $key ],
950
+ 'FileUrl' => $url,
951
+ $editor_link['name'] => $editor_link['value'],
952
+ )
953
+ );
954
+ }
955
+ $result = 1;
956
+ }
957
+ }
958
+
959
+ $removed_urls = array_diff( $this->_old_file_urls, $new_file_urls );
960
+ // Removed files from the product.
961
+ if ( count( $removed_urls ) > 0 ) {
962
+ // If the file has only changed URL.
963
+ if ( count( $new_file_urls ) == count( $this->_old_file_urls ) ) {
964
+ $is_url_changed = true;
965
+ } else {
966
+ foreach ( $removed_urls as $key => $url ) {
967
+ $this->plugin->alerts->Trigger(
968
+ 9024, array(
969
+ 'ProductTitle' => $oldpost->post_title,
970
+ 'FileName' => $this->_old_file_names[ $key ],
971
+ 'FileUrl' => $url,
972
+ $editor_link['name'] => $editor_link['value'],
973
+ )
974
+ );
975
+ }
976
+ $result = 1;
977
+ }
978
+ }
979
+
980
+ $added_names = array_diff( $new_file_names, $this->_old_file_names );
981
+ if ( count( $added_names ) > 0 ) {
982
+ // If the file has only changed Name.
983
+ if ( count( $new_file_names ) == count( $this->_old_file_names ) ) {
984
+ foreach ( $added_names as $key => $name ) {
985
+ $this->plugin->alerts->Trigger(
986
+ 9025, array(
987
+ 'ProductTitle' => $oldpost->post_title,
988
+ 'OldName' => $this->_old_file_names[ $key ],
989
+ 'NewName' => $name,
990
+ $editor_link['name'] => $editor_link['value'],
991
+ )
992
+ );
993
+ }
994
+ $result = 1;
995
+ }
996
+ }
997
+
998
+ if ( $is_url_changed ) {
999
+ foreach ( $added_urls as $key => $url ) {
1000
+ $this->plugin->alerts->Trigger(
1001
+ 9026, array(
1002
+ 'ProductTitle' => $oldpost->post_title,
1003
+ 'FileName' => $new_file_names[ $key ],
1004
+ 'OldUrl' => $removed_urls[ $key ],
1005
+ 'NewUrl' => $url,
1006
+ $editor_link['name'] => $editor_link['value'],
1007
+ )
1008
+ );
1009
+ }
1010
+ $result = 1;
1011
+ }
1012
+ return $result;
1013
+ }
1014
+
1015
+ /**
1016
+ * Trigger events Settings: 9027, 9028, 9029, 9030, 9031, 9032, 9033
1017
+ */
1018
+ protected function CheckSettingsChange() {
1019
+ // Filter POST and GET global arrays.
1020
+ $post_array = filter_input_array( INPUT_POST );
1021
+ $get_array = filter_input_array( INPUT_GET );
1022
+
1023
+ if ( isset( $post_array['_wpnonce'] )
1024
+ && ! wp_verify_nonce( $post_array['_wpnonce'], 'woocommerce-settings' ) ) {
1025
+ return false;
1026
+ }
1027
+
1028
+ if ( isset( $get_array['page'] ) && 'wc-settings' == $get_array['page'] ) {
1029
+ if ( isset( $get_array['tab'] ) && 'products' == $get_array['tab'] ) {
1030
+ if ( isset( $post_array['woocommerce_weight_unit'] ) ) {
1031
+ $old_unit = $this->GetConfig( 'weight_unit' );
1032
+ $new_unit = $post_array['woocommerce_weight_unit'];
1033
+ if ( $old_unit != $new_unit ) {
1034
+ $this->plugin->alerts->Trigger(
1035
+ 9027, array(
1036
+ 'OldUnit' => $old_unit,
1037
+ 'NewUnit' => $new_unit,
1038
+ )
1039
+ );
1040
+ }
1041
+ }
1042
+ if ( isset( $post_array['woocommerce_dimension_unit'] ) ) {
1043
+ $old_unit = $this->GetConfig( 'dimension_unit' );
1044
+ $new_unit = $post_array['woocommerce_dimension_unit'];
1045
+ if ( $old_unit != $new_unit ) {
1046
+ $this->plugin->alerts->Trigger(
1047
+ 9028, array(
1048
+ 'OldUnit' => $old_unit,
1049
+ 'NewUnit' => $new_unit,
1050
+ )
1051
+ );
1052
+ }
1053
+ }
1054
+ } elseif ( isset( $get_array['tab'] ) && 'checkout' == $get_array['tab'] ) {
1055
+ if ( ! empty( $post_array ) && '' == $get_array['section'] ) {
1056
+ $old_enable_coupons = $this->GetConfig( 'enable_coupons' );
1057
+ $new_enable_coupons = isset( $post_array['woocommerce_enable_coupons'] ) ? 'yes' : 'no';
1058
+ if ( $old_enable_coupons != $new_enable_coupons ) {
1059
+ $status = ( 'yes' == $new_enable_coupons ) ? 'Enabled' : 'Disabled';
1060
+ $this->plugin->alerts->Trigger(
1061
+ 9032, array(
1062
+ 'Status' => $status,
1063
+ )
1064
+ );
1065
+ }
1066
+ $old_enable_guest_checkout = $this->GetConfig( 'enable_guest_checkout' );
1067
+ $new_enable_guest_checkout = isset( $post_array['woocommerce_enable_guest_checkout'] ) ? 'yes' : 'no';
1068
+ if ( $old_enable_guest_checkout != $new_enable_guest_checkout ) {
1069
+ $status = ( 'yes' == $new_enable_guest_checkout ) ? 'Enabled' : 'Disabled';
1070
+ $this->plugin->alerts->Trigger(
1071
+ 9033, array(
1072
+ 'Status' => $status,
1073
+ )
1074
+ );
1075
+ }
1076
+ } elseif ( ! empty( $post_array ) && 'cod' === $get_array['section'] ) {
1077
+ $old_cash_on_delivery = $this->GetConfig( 'cod_settings' );
1078
+ $old_cash_on_delivery = isset( $old_cash_on_delivery['enabled'] ) ? $old_cash_on_delivery['enabled'] : '';
1079
+ $new_cash_on_delivery = isset( $post_array['woocommerce_cod_enabled'] ) ? 'yes' : 'no';
1080
+ if ( $old_cash_on_delivery !== $new_cash_on_delivery ) {
1081
+ $status = ( 'yes' === $new_cash_on_delivery ) ? 'Enabled' : 'Disabled';
1082
+ $this->plugin->alerts->Trigger(
1083
+ 9034, array(
1084
+ 'Status' => $status,
1085
+ )
1086
+ );
1087
+ }
1088
+ }
1089
+ } else {
1090
+ if ( isset( $post_array['woocommerce_default_country'] ) ) {
1091
+ $old_location = $this->GetConfig( 'default_country' );
1092
+ $new_location = $post_array['woocommerce_default_country'];
1093
+ if ( $old_location != $new_location ) {
1094
+ $this->plugin->alerts->Trigger(
1095
+ 9029, array(
1096
+ 'OldLocation' => $old_location,
1097
+ 'NewLocation' => $new_location,
1098
+ )
1099
+ );
1100
+ }
1101
+ $old_calc_taxes = $this->GetConfig( 'calc_taxes' );
1102
+ $new_calc_taxes = isset( $post_array['woocommerce_calc_taxes'] ) ? 'yes' : 'no';
1103
+ if ( $old_calc_taxes != $new_calc_taxes ) {
1104
+ $status = ( 'yes' == $new_calc_taxes ) ? 'Enabled' : 'Disabled';
1105
+ $this->plugin->alerts->Trigger(
1106
+ 9030, array(
1107
+ 'Status' => $status,
1108
+ )
1109
+ );
1110
+ }
1111
+ }
1112
+ if ( isset( $post_array['woocommerce_currency'] ) ) {
1113
+ $old_currency = $this->GetConfig( 'currency' );
1114
+ $new_currency = $post_array['woocommerce_currency'];
1115
+ if ( $old_currency != $new_currency ) {
1116
+ $this->plugin->alerts->Trigger(
1117
+ 9031, array(
1118
+ 'OldCurrency' => $old_currency,
1119
+ 'NewCurrency' => $new_currency,
1120
+ )
1121
+ );
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+ }
1127
+
1128
+ /**
1129
+ * Get Stock Status Name.
1130
+ *
1131
+ * @param string $slug - Stock slug.
1132
+ * @return string
1133
+ */
1134
+ private function GetStockStatusName( $slug ) {
1135
+ if ( 'instock' == $slug ) {
1136
+ return __( 'In stock', 'wp-security-audit-log' );
1137
+ } elseif ( 'outofstock' == $slug ) {
1138
+ return __( 'Out of stock', 'wp-security-audit-log' );
1139
+ }
1140
+ }
1141
+
1142
+ /**
1143
+ * Return: Product Categories.
1144
+ *
1145
+ * @param object $post - Product post object.
1146
+ * @return array
1147
+ */
1148
+ protected function GetProductCategories( $post ) {
1149
+ return wp_get_post_terms(
1150
+ $post->ID, 'product_cat', array(
1151
+ 'fields' => 'names',
1152
+ )
1153
+ );
1154
+ }
1155
+
1156
+ /**
1157
+ * Return: Product Data.
1158
+ *
1159
+ * @param object $post - Product post object.
1160
+ * @return array
1161
+ */
1162
+ protected function GetProductData( $post ) {
1163
+ return wp_get_post_terms(
1164
+ $post->ID, 'product_type', array(
1165
+ 'fields' => 'names',
1166
+ )
1167
+ );
1168
+ }
1169
+
1170
+ /**
1171
+ * Get the config setting
1172
+ *
1173
+ * @param string $option_name - Option Name.
1174
+ * @return string
1175
+ */
1176
+ private function GetConfig( $option_name ) {
1177
+ $fn = $this->IsMultisite() ? 'get_site_option' : 'get_option';
1178
+ return $fn( 'woocommerce_' . $option_name );
1179
+ }
1180
+
1181
+ /**
1182
+ * Check post type.
1183
+ *
1184
+ * @param stdClass $post - Post.
1185
+ * @return bool
1186
+ */
1187
+ private function CheckWooCommerce( $post ) {
1188
+ switch ( $post->post_type ) {
1189
+ case 'product':
1190
+ return true;
1191
+ default:
1192
+ return false;
1193
+ }
1194
+ }
1195
+
1196
+ /**
1197
+ * Get editor link.
1198
+ *
1199
+ * @param stdClass $post - The post.
1200
+ * @return array $editor_link - Name and value link.
1201
+ */
1202
+ private function GetEditorLink( $post ) {
1203
+ $name = 'EditorLinkProduct';
1204
+ $value = get_edit_post_link( $post->ID );
1205
+ $editor_link = array(
1206
+ 'name' => $name,
1207
+ 'value' => $value,
1208
+ );
1209
+ return $editor_link;
1210
+ }
1211
+
1212
+ /**
1213
+ * Get Currency symbol.
1214
+ *
1215
+ * @param string $currency - Currency (default: '').
1216
+ * @return string
1217
+ */
1218
+ private function GetCurrencySymbol( $currency = '' ) {
1219
+ $symbols = array(
1220
+ 'AED' => '&#x62f;.&#x625;',
1221
+ 'AFN' => '&#x60b;',
1222
+ 'ALL' => 'L',
1223
+ 'AMD' => 'AMD',
1224
+ 'ANG' => '&fnof;',
1225
+ 'AOA' => 'Kz',
1226
+ 'ARS' => '&#36;',
1227
+ 'AUD' => '&#36;',
1228
+ 'AWG' => '&fnof;',
1229
+ 'AZN' => 'AZN',
1230
+ 'BAM' => 'KM',
1231
+ 'BBD' => '&#36;',
1232
+ 'BDT' => '&#2547;&nbsp;',
1233
+ 'BGN' => '&#1083;&#1074;.',
1234
+ 'BHD' => '.&#x62f;.&#x628;',
1235
+ 'BIF' => 'Fr',
1236
+ 'BMD' => '&#36;',
1237
+ 'BND' => '&#36;',
1238
+ 'BOB' => 'Bs.',
1239
+ 'BRL' => '&#82;&#36;',
1240
+ 'BSD' => '&#36;',
1241
+ 'BTC' => '&#3647;',
1242
+ 'BTN' => 'Nu.',
1243
+ 'BWP' => 'P',
1244
+ 'BYR' => 'Br',
1245
+ 'BZD' => '&#36;',
1246
+ 'CAD' => '&#36;',
1247
+ 'CDF' => 'Fr',
1248
+ 'CHF' => '&#67;&#72;&#70;',
1249
+ 'CLP' => '&#36;',
1250
+ 'CNY' => '&yen;',
1251
+ 'COP' => '&#36;',
1252
+ 'CRC' => '&#x20a1;',
1253
+ 'CUC' => '&#36;',
1254
+ 'CUP' => '&#36;',
1255
+ 'CVE' => '&#36;',
1256
+ 'CZK' => '&#75;&#269;',
1257
+ 'DJF' => 'Fr',
1258
+ 'DKK' => 'DKK',
1259
+ 'DOP' => 'RD&#36;',
1260
+ 'DZD' => '&#x62f;.&#x62c;',
1261
+ 'EGP' => 'EGP',
1262
+ 'ERN' => 'Nfk',
1263
+ 'ETB' => 'Br',
1264
+ 'EUR' => '&euro;',
1265
+ 'FJD' => '&#36;',
1266
+ 'FKP' => '&pound;',
1267
+ 'GBP' => '&pound;',
1268
+ 'GEL' => '&#x10da;',
1269
+ 'GGP' => '&pound;',
1270
+ 'GHS' => '&#x20b5;',
1271
+ 'GIP' => '&pound;',
1272
+ 'GMD' => 'D',
1273
+ 'GNF' => 'Fr',
1274
+ 'GTQ' => 'Q',
1275
+ 'GYD' => '&#36;',
1276
+ 'HKD' => '&#36;',
1277
+ 'HNL' => 'L',
1278
+ 'HRK' => 'Kn',
1279
+ 'HTG' => 'G',
1280
+ 'HUF' => '&#70;&#116;',
1281
+ 'IDR' => 'Rp',
1282
+ 'ILS' => '&#8362;',
1283
+ 'IMP' => '&pound;',
1284
+ 'INR' => '&#8377;',
1285
+ 'IQD' => '&#x639;.&#x62f;',
1286
+ 'IRR' => '&#xfdfc;',
1287
+ 'ISK' => 'kr.',
1288
+ 'JEP' => '&pound;',
1289
+ 'JMD' => '&#36;',
1290
+ 'JOD' => '&#x62f;.&#x627;',
1291
+ 'JPY' => '&yen;',
1292
+ 'KES' => 'KSh',
1293
+ 'KGS' => '&#x441;&#x43e;&#x43c;',
1294
+ 'KHR' => '&#x17db;',
1295
+ 'KMF' => 'Fr',
1296
+ 'KPW' => '&#x20a9;',
1297
+ 'KRW' => '&#8361;',
1298
+ 'KWD' => '&#x62f;.&#x643;',
1299
+ 'KYD' => '&#36;',
1300
+ 'KZT' => 'KZT',
1301
+ 'LAK' => '&#8365;',
1302
+ 'LBP' => '&#x644;.&#x644;',
1303
+ 'LKR' => '&#xdbb;&#xdd4;',
1304
+ 'LRD' => '&#36;',
1305
+ 'LSL' => 'L',
1306
+ 'LYD' => '&#x644;.&#x62f;',
1307
+ 'MAD' => '&#x62f;. &#x645;.',
1308
+ 'MAD' => '&#x62f;.&#x645;.',
1309
+ 'MDL' => 'L',
1310
+ 'MGA' => 'Ar',
1311
+ 'MKD' => '&#x434;&#x435;&#x43d;',
1312
+ 'MMK' => 'Ks',
1313
+ 'MNT' => '&#x20ae;',
1314
+ 'MOP' => 'P',
1315
+ 'MRO' => 'UM',
1316
+ 'MUR' => '&#x20a8;',
1317
+ 'MVR' => '.&#x783;',
1318
+ 'MWK' => 'MK',
1319
+ 'MXN' => '&#36;',
1320
+ 'MYR' => '&#82;&#77;',
1321
+ 'MZN' => 'MT',
1322
+ 'NAD' => '&#36;',
1323
+ 'NGN' => '&#8358;',
1324
+ 'NIO' => 'C&#36;',
1325
+ 'NOK' => '&#107;&#114;',
1326
+ 'NPR' => '&#8360;',
1327
+ 'NZD' => '&#36;',
1328
+ 'OMR' => '&#x631;.&#x639;.',
1329
+ 'PAB' => 'B/.',
1330
+ 'PEN' => 'S/.',
1331
+ 'PGK' => 'K',
1332
+ 'PHP' => '&#8369;',
1333
+ 'PKR' => '&#8360;',
1334
+ 'PLN' => '&#122;&#322;',
1335
+ 'PRB' => '&#x440;.',
1336
+ 'PYG' => '&#8370;',
1337
+ 'QAR' => '&#x631;.&#x642;',
1338
+ 'RMB' => '&yen;',
1339
+ 'RON' => 'lei',
1340
+ 'RSD' => '&#x434;&#x438;&#x43d;.',
1341
+ 'RUB' => '&#8381;',
1342
+ 'RWF' => 'Fr',
1343
+ 'SAR' => '&#x631;.&#x633;',
1344
+ 'SBD' => '&#36;',
1345
+ 'SCR' => '&#x20a8;',
1346
+ 'SDG' => '&#x62c;.&#x633;.',
1347
+ 'SEK' => '&#107;&#114;',
1348
+ 'SGD' => '&#36;',
1349
+ 'SHP' => '&pound;',
1350
+ 'SLL' => 'Le',
1351
+ 'SOS' => 'Sh',
1352
+ 'SRD' => '&#36;',
1353
+ 'SSP' => '&pound;',
1354
+ 'STD' => 'Db',
1355
+ 'SYP' => '&#x644;.&#x633;',
1356
+ 'SZL' => 'L',
1357
+ 'THB' => '&#3647;',
1358
+ 'TJS' => '&#x405;&#x41c;',
1359
+ 'TMT' => 'm',
1360
+ 'TND' => '&#x62f;.&#x62a;',
1361
+ 'TOP' => 'T&#36;',
1362
+ 'TRY' => '&#8378;',
1363
+ 'TTD' => '&#36;',
1364
+ 'TWD' => '&#78;&#84;&#36;',
1365
+ 'TZS' => 'Sh',
1366
+ 'UAH' => '&#8372;',
1367
+ 'UGX' => 'UGX',
1368
+ 'USD' => '&#36;',
1369
+ 'UYU' => '&#36;',
1370
+ 'UZS' => 'UZS',
1371
+ 'VEF' => 'Bs F',
1372
+ 'VND' => '&#8363;',
1373
+ 'VUV' => 'Vt',
1374
+ 'WST' => 'T',
1375
+ 'XAF' => 'Fr',
1376
+ 'XCD' => '&#36;',
1377
+ 'XOF' => 'Fr',
1378
+ 'XPF' => 'Fr',
1379
+ 'YER' => '&#xfdfc;',
1380
+ 'ZAR' => '&#82;',
1381
+ 'ZMW' => 'ZK',
1382
+ );
1383
+ $currency_symbol = isset( $symbols[ $currency ] ) ? $symbols[ $currency ] : '';
1384
+
1385
+ return $currency_symbol;
1386
+ }
1387
  }
classes/Settings.php CHANGED
@@ -1,17 +1,31 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
5
  * This class is the actual controller of the Settings Page.
 
 
6
  */
7
- class WSAL_Settings
8
- {
9
  /**
 
 
10
  * @var WpSecurityAuditLog
11
  */
12
  protected $_plugin;
13
 
14
- // <editor-fold desc="Developer Options">
15
  const OPT_DEV_DATA_INSPECTOR = 'd';
16
  const OPT_DEV_PHP_ERRORS = 'p';
17
  const OPT_DEV_REQUEST_LOG = 'r';
@@ -19,30 +33,66 @@ class WSAL_Settings
19
 
20
  const ERROR_CODE_INVALID_IP = 901;
21
 
 
 
 
 
 
22
  protected $_devoption = null;
23
 
 
 
 
 
 
24
  protected $_pruning = 0;
25
 
 
 
 
 
 
26
  protected $_disabled = null;
27
 
 
 
 
 
 
28
  protected $_viewers = null;
29
 
 
 
 
 
 
30
  protected $_editors = null;
31
 
 
 
 
 
 
32
  protected $_perpage = null;
33
 
34
  /**
35
- * Users excluded from monitoring
 
 
36
  */
37
  protected $_excluded_users = array();
38
 
39
  /**
40
- * Roles excluded from monitoring
 
 
41
  */
42
- protected $_excluded_roles = array();
43
 
44
  /**
45
- * Custom fields excluded from monitoring
 
 
46
  */
47
  protected $_excluded_custom = array();
48
 
@@ -54,180 +104,196 @@ class WSAL_Settings
54
  protected $_post_types = array();
55
 
56
  /**
57
- * IP excluded from monitoring
 
 
58
  */
59
  protected $_excluded_ip = array();
60
 
61
- public function __construct(WpSecurityAuditLog $plugin)
62
- {
 
 
 
 
63
  $this->_plugin = $plugin;
64
  }
65
 
66
  /**
67
- * @return array Array of developer options to be enabled by default.
 
 
68
  */
69
- public function GetDefaultDevOptions()
70
- {
71
  return array();
72
  }
73
 
74
  /**
75
  * Returns whether a developer option is enabled or not.
76
- * @param string $option See self::OPT_DEV_* constants.
77
- * @return boolean If option is enabled or not.
 
78
  */
79
- public function IsDevOptionEnabled($option)
80
- {
81
- if (is_null($this->_devoption)) {
82
  $this->_devoption = $this->_plugin->GetGlobalOption(
83
  'dev-options',
84
- implode(',', $this->GetDefaultDevOptions())
85
  );
86
- $this->_devoption = explode(',', $this->_devoption);
87
  }
88
- return in_array($option, $this->_devoption);
89
  }
90
 
91
  /**
92
- * @return boolean Whether any developer option has been enabled or not.
 
 
93
  */
94
- public function IsAnyDevOptionEnabled()
95
- {
96
- return !!$this->_plugin->GetGlobalOption('dev-options', null);
97
  }
98
 
99
  /**
100
  * Sets whether a developer option is enabled or not.
101
- * @param string $option See self::OPT_DEV_* constants.
102
- * @param boolean $enabled If option should be enabled or not.
 
103
  */
104
- public function SetDevOptionEnabled($option, $enabled)
105
- {
106
- // make sure options have been loaded
107
- $this->IsDevOptionEnabled('');
108
- // remove option if it exists
109
- while (($p = array_search($option, $this->_devoption)) !== false) {
110
- unset($this->_devoption[$p]);
111
  }
112
- // add option if callee wants it enabled
113
- if ($enabled) {
114
  $this->_devoption[] = $option;
115
  }
116
- // commit option
117
  $this->_plugin->SetGlobalOption(
118
  'dev-options',
119
- implode(',', $this->_devoption)
120
  );
121
  }
122
 
123
  /**
124
  * Remove all enabled developer options.
125
  */
126
- public function ClearDevOptions()
127
- {
128
  $this->_devoption = array();
129
- $this->_plugin->SetGlobalOption('dev-options', '');
130
  }
131
 
132
  /**
133
- * @return boolean Whether to enable data inspector or not.
 
 
134
  */
135
- public function IsDataInspectorEnabled()
136
- {
137
- return $this->IsDevOptionEnabled(self::OPT_DEV_DATA_INSPECTOR);
138
  }
139
 
140
  /**
141
- * @return boolean Whether to PHP error logging or not.
 
 
142
  */
143
- public function IsPhpErrorLoggingEnabled()
144
- {
145
- return $this->IsDevOptionEnabled(self::OPT_DEV_PHP_ERRORS);
146
  }
147
 
148
  /**
149
- * @return boolean Whether to log requests to file or not.
 
 
150
  */
151
- public function IsRequestLoggingEnabled()
152
- {
153
- return $this->IsDevOptionEnabled(self::OPT_DEV_REQUEST_LOG);
154
  }
155
 
156
  /**
157
- * @return boolean Whether to store debug backtrace for PHP alerts or not.
 
 
158
  */
159
- public function IsBacktraceLoggingEnabled()
160
- {
161
- return $this->IsDevOptionEnabled(self::OPT_DEV_BACKTRACE_LOG);
162
  }
163
 
164
- // </editor-fold>
165
-
166
  /**
167
- * @return boolean Whether dashboard widgets are enabled or not.
 
 
168
  */
169
- public function IsWidgetsEnabled()
170
- {
171
- return !$this->_plugin->GetGlobalOption('disable-widgets');
172
  }
173
 
174
  /**
175
- * @param boolean $newvalue Whether dashboard widgets are enabled or not.
 
 
176
  */
177
- public function SetWidgetsEnabled($newvalue)
178
- {
179
- $this->_plugin->SetGlobalOption('disable-widgets', !$newvalue);
180
  }
181
 
182
  /**
183
- * @return boolean Whether alerts in audit log view refresh automatically or not.
 
 
184
  */
185
- public function IsRefreshAlertsEnabled()
186
- {
187
- return !$this->_plugin->GetGlobalOption('disable-refresh');
188
  }
189
 
190
  /**
191
- * @param boolean $newvalue Whether alerts in audit log view refresh automatically or not.
 
 
192
  */
193
- public function SetRefreshAlertsEnabled($newvalue)
194
- {
195
- $this->_plugin->SetGlobalOption('disable-refresh', !$newvalue);
196
  }
197
 
198
  /**
199
- * @return int Maximum number of alerts to show in dashboard widget.
 
 
200
  */
201
- public function GetDashboardWidgetMaxAlerts()
202
- {
203
  return 5;
204
  }
205
 
206
- // <editor-fold desc="Pruning Settings">
207
  /**
208
- * @return int The maximum number of alerts allowable.
 
 
209
  */
210
- public function GetMaxAllowedAlerts()
211
- {
212
  return 5000;
213
  }
214
 
215
  /**
216
- * @return string The default pruning date.
 
 
217
  */
218
- public function GetDefaultPruningDate()
219
- {
220
- return '1 month';
221
  }
222
 
223
  /**
224
- * @return string The current pruning date.
 
 
225
  */
226
- public function GetPruningDate()
227
- {
228
- if (!$this->_pruning) {
229
- $this->_pruning = $this->_plugin->GetGlobalOption('pruning-date');
230
- if (!strtotime($this->_pruning)) {
231
  $this->_pruning = $this->GetDefaultPruningDate();
232
  }
233
  }
@@ -235,217 +301,210 @@ class WSAL_Settings
235
  }
236
 
237
  /**
238
- * @param string $newvalue The new pruning date.
 
 
239
  */
240
- public function SetPruningDate($newvalue)
241
- {
242
- if (strtotime($newvalue)) {
243
- $this->_plugin->SetGlobalOption('pruning-date', $newvalue);
244
  $this->_pruning = $newvalue;
245
  }
246
  }
247
 
248
  /**
249
- * @return integer Maximum number of alerts to keep.
 
 
250
  */
251
- public function GetPruningLimit()
252
- {
253
- $val = (int)$this->_plugin->GetGlobalOption('pruning-limit');
254
  return $val ? $val : $this->GetMaxAllowedAlerts();
255
  }
256
 
257
  /**
258
- * @param integer $newvalue The new maximum number of alerts.
 
 
259
  */
260
- public function SetPruningLimit($newvalue)
261
- {
262
- $newvalue = max(/*min(*/(int)$newvalue/*, $this->GetMaxAllowedAlerts())*/, 1);
263
- $this->_plugin->SetGlobalOption('pruning-limit', $newvalue);
264
  }
265
 
266
- public function SetPruningDateEnabled($enabled)
267
- {
268
- $this->_plugin->SetGlobalOption('pruning-date-e', $enabled);
269
  }
270
 
271
- public function SetPruningLimitEnabled($enabled)
272
- {
273
- $this->_plugin->SetGlobalOption('pruning-limit-e', $enabled);
274
  }
275
 
276
- public function IsPruningDateEnabled(){
277
- return $this->_plugin->GetGlobalOption('pruning-date-e');
278
  }
279
 
280
- public function IsPruningLimitEnabled()
281
- {
282
- return $this->_plugin->GetGlobalOption('pruning-limit-e');
283
  }
284
 
285
- public function IsRestrictAdmins()
286
- {
287
- return $this->_plugin->GetGlobalOption('restrict-admins', false);
288
  }
289
 
290
  /**
291
- * @deprecated Sandbox functionality is now in an external plugin.
 
 
292
  */
293
- public function IsSandboxPageEnabled()
294
- {
295
- $plugins = $this->_plugin->licensing->plugins();
296
- return isset($plugins['wsal-sandbox-extensionphp']);
297
  }
298
 
299
- public function SetRestrictAdmins($enable)
300
- {
301
- $this->_plugin->SetGlobalOption('restrict-admins', (bool)$enable);
302
  }
303
 
304
- // </editor-fold>
305
- public function GetDefaultDisabledAlerts()
306
- {
307
- return array(0000, 0001, 0002, 0003, 0004, 0005);
308
  }
309
 
310
  /**
311
- * @return array IDs of disabled alerts.
 
 
312
  */
313
- public function GetDisabledAlerts()
314
- {
315
- if (!$this->_disabled) {
316
- $this->_disabled = implode(',', $this->GetDefaultDisabledAlerts());
317
- $this->_disabled = $this->_plugin->GetGlobalOption('disabled-alerts', $this->_disabled);
318
- $this->_disabled = ($this->_disabled == '') ? array() : explode(',', $this->_disabled);
319
- $this->_disabled = array_map('intval', $this->_disabled);
320
  }
321
  return $this->_disabled;
322
  }
323
 
324
  /**
 
 
325
  * @param array $types IDs alerts to disable.
326
  */
327
- public function SetDisabledAlerts($types)
328
- {
329
- $this->_disabled = array_unique(array_map('intval', $types));
330
- $this->_plugin->SetGlobalOption('disabled-alerts', implode(',', $this->_disabled));
331
  }
332
 
333
- public function IsIncognito()
334
- {
335
- return $this->_plugin->GetGlobalOption('hide-plugin');
336
  }
337
 
338
- public function SetIncognito($enabled)
339
- {
340
- return $this->_plugin->SetGlobalOption('hide-plugin', $enabled);
341
  }
342
 
343
  /**
344
  * Checking if Logging is enabled.
345
  */
346
- public function IsLoggingDisabled()
347
- {
348
- return $this->_plugin->GetGlobalOption('disable-logging');
349
  }
350
 
351
- public function SetLoggingDisabled($disabled)
352
- {
353
- return $this->_plugin->SetGlobalOption('disable-logging', $disabled);
354
  }
355
 
356
  /**
357
  * Checking if the data will be removed.
358
  */
359
- public function IsDeleteData()
360
- {
361
- return $this->_plugin->GetGlobalOption('delete-data');
362
  }
363
 
364
- public function SetDeleteData($enabled)
365
- {
366
- return $this->_plugin->SetGlobalOption('delete-data', $enabled);
367
  }
368
 
369
- // <editor-fold desc="Access Control">
370
-
371
- public function SetAllowedPluginViewers($usersOrRoles)
372
- {
373
- $this->_viewers = $usersOrRoles;
374
- $this->_plugin->SetGlobalOption('plugin-viewers', implode(',', $this->_viewers));
375
  }
376
 
377
- public function GetAllowedPluginViewers()
378
- {
379
- if (is_null($this->_viewers)) {
380
- $this->_viewers = array_unique(array_filter(explode(',', $this->_plugin->GetGlobalOption('plugin-viewers'))));
381
  }
382
  return $this->_viewers;
383
  }
384
 
385
- public function SetAllowedPluginEditors($usersOrRoles)
386
- {
387
- $this->_editors = $usersOrRoles;
388
- $this->_plugin->SetGlobalOption('plugin-editors', implode(',', $this->_editors));
389
  }
390
 
391
- public function GetAllowedPluginEditors()
392
- {
393
- if (is_null($this->_editors)) {
394
- $this->_editors = array_unique(array_filter(explode(',', $this->_plugin->GetGlobalOption('plugin-editors'))));
395
  }
396
  return $this->_editors;
397
  }
398
 
399
- public function SetViewPerPage($newvalue)
400
- {
401
- $this->_perpage = max($newvalue, 1);
402
- $this->_plugin->SetGlobalOption('items-per-page', $this->_perpage);
403
  }
404
 
405
- public function GetViewPerPage()
406
- {
407
- if (is_null($this->_perpage)) {
408
- $this->_perpage = (int)$this->_plugin->GetGlobalOption('items-per-page', 10);
409
  }
410
  return $this->_perpage;
411
  }
412
 
413
  /**
 
 
414
  * @param string $action Type of action, either 'view' or 'edit'.
415
  * @return boolean If user has access or not.
416
  */
417
- public function CurrentUserCan($action)
418
- {
419
- return $this->UserCan(wp_get_current_user(), $action);
420
  }
421
 
422
  /**
423
- * @return string[] List of superadmin usernames.
 
 
424
  */
425
- protected function GetSuperAdmins()
426
- {
427
  return $this->_plugin->IsMultisite() ? get_super_admins() : array();
428
  }
429
 
430
  /**
431
- * @return string[] List of admin usernames.
 
 
432
  */
433
- protected function GetAdmins()
434
- {
435
- if ($this->_plugin->IsMultisite()) {
436
- // see: https://gist.github.com/1508426/65785a15b8638d43a9905effb59e4d97319ef8f8
 
 
 
437
  global $wpdb;
438
- $cap = $wpdb->prefix."capabilities";
439
  $sql = "SELECT DISTINCT $wpdb->users.user_login"
440
  . " FROM $wpdb->users"
441
  . " INNER JOIN $wpdb->usermeta ON ($wpdb->users.ID = $wpdb->usermeta.user_id )"
442
  . " WHERE $wpdb->usermeta.meta_key = '$cap'"
443
  . " AND CAST($wpdb->usermeta.meta_value AS CHAR) LIKE '%\"administrator\"%'";
444
- return $wpdb->get_col($sql);
445
  } else {
446
  $result = array();
447
  $query = 'role=administrator&fields[]=user_login';
448
- foreach (get_users($query) as $user) {
449
  $result[] = $user->user_login;
450
  }
451
  return $result;
@@ -454,202 +513,180 @@ class WSAL_Settings
454
 
455
  /**
456
  * Returns access tokens for a particular action.
457
- * @param string $action Type of action.
 
458
  * @return string[] List of tokens (usernames, roles etc).
459
  */
460
- public function GetAccessTokens($action)
461
- {
462
  $allowed = array();
463
- switch ($action) {
464
  case 'view':
465
  $allowed = $this->GetAllowedPluginViewers();
466
- $allowed = array_merge($allowed, $this->GetAllowedPluginEditors());
467
- if (!$this->IsRestrictAdmins()) {
468
- $allowed = array_merge($allowed, $this->GetSuperAdmins());
469
- $allowed = array_merge($allowed, $this->GetAdmins());
470
  }
471
  break;
472
  case 'edit':
473
  $allowed = $this->GetAllowedPluginEditors();
474
- if (!$this->IsRestrictAdmins()) {
475
- $allowed = array_merge($allowed, $this->_plugin->IsMultisite() ? $this->GetSuperAdmins() : $this->GetAdmins());
476
  }
477
  break;
478
  default:
479
- throw new Exception('Unknown action "'.$action.'".');
480
  }
481
- if (!$this->IsRestrictAdmins()) {
482
- if (is_multisite()) {
483
- $allowed = array_merge($allowed, get_super_admins());
484
  } else {
485
  $allowed[] = 'administrator';
486
  }
487
  }
488
- return array_unique($allowed);
489
  }
490
 
491
  /**
492
- * @param integer|WP_user $user User object to check.
493
- * @param string $action Type of action, either 'view' or 'edit'.
 
 
494
  * @return boolean If user has access or not.
495
  */
496
- public function UserCan($user, $action)
497
- {
498
- if (is_int($user)) {
499
- $user = get_userdata($user);
500
  }
501
- $allowed = $this->GetAccessTokens($action);
502
- $check = array_merge($user->roles, array($user->user_login));
503
- foreach ($check as $item) {
504
- if (in_array($item, $allowed)) {
505
  return true;
506
  }
507
  }
508
  return false;
509
  }
510
 
511
- public function GetCurrentUserRoles($baseRoles = null)
512
- {
513
- if ($baseRoles == null) {
514
- $baseRoles = wp_get_current_user()->roles;
515
  }
516
- if (function_exists('is_super_admin') && is_super_admin()) {
517
- $baseRoles[] = 'superadmin';
518
  }
519
- return $baseRoles;
520
  }
521
 
522
- public function IsLoginSuperAdmin($username)
523
- {
524
- $userId = username_exists($username);
525
- if (function_exists('is_super_admin') && is_super_admin($userId)) {
526
  return true;
527
  } else {
528
  return false;
529
  }
530
  }
531
 
532
- // </editor-fold>
533
-
534
- // <editor-fold desc="Licensing">
535
- public function GetLicenses()
536
- {
537
- return $this->_plugin->GetGlobalOption('licenses');
538
  }
539
 
540
- public function GetLicense($name)
541
- {
542
  $data = $this->GetLicenses();
543
- $name = sanitize_key(basename($name));
544
- return isset($data[$name]) ? $data[$name] : array();
545
  }
546
 
547
- public function SetLicenses($data)
548
- {
549
- $this->_plugin->SetGlobalOption('licenses', $data);
550
  }
551
 
552
- public function GetLicenseKey($name)
553
- {
554
- $data = $this->GetLicense($name);
555
- return isset($data['key']) ? $data['key'] : '';
556
  }
557
 
558
- public function GetLicenseStatus($name){
559
- $data = $this->GetLicense($name);
560
- return isset($data['sts']) ? $data['sts'] : '';
561
  }
562
 
563
- public function GetLicenseErrors($name)
564
- {
565
- $data = $this->GetLicense($name);
566
- return isset($data['err']) ? $data['err'] : '';
567
  }
568
 
569
- public function SetLicenseKey($name, $key)
570
- {
571
  $data = $this->GetLicenses();
572
- if (!isset($data[$name])) {
573
- $data[$name] = array();
574
  }
575
- $data[$name]['key'] = $key;
576
- $this->SetLicenses($data);
577
  }
578
 
579
- public function SetLicenseStatus($name, $status)
580
- {
581
  $data = $this->GetLicenses();
582
- if (!isset($data[$name])) {
583
- $data[$name] = array();
584
  }
585
- $data[$name]['sts'] = $status;
586
- $this->SetLicenses($data);
587
  }
588
 
589
- public function SetLicenseErrors($name, $errors)
590
- {
591
  $data = $this->GetLicenses();
592
- if (!isset($data[$name])) {
593
- $data[$name] = array();
594
  }
595
- $data[$name]['err'] = $errors;
596
- $this->SetLicenses($data);
597
  }
598
 
599
- public function ClearLicenses()
600
- {
601
- $this->SetLicenses(array());
602
  }
603
 
604
- // </editor-fold>
605
- // <editor-fold desc="Client IP Retrieval">
606
-
607
- public function IsMainIPFromProxy()
608
- {
609
- return $this->_plugin->GetGlobalOption('use-proxy-ip');
610
  }
611
 
612
- public function SetMainIPFromProxy($enabled)
613
- {
614
- return $this->_plugin->SetGlobalOption('use-proxy-ip', $enabled);
615
  }
616
 
617
- public function IsInternalIPsFiltered()
618
- {
619
- return $this->_plugin->GetGlobalOption('filter-internal-ip');
620
  }
621
 
622
- public function SetInternalIPsFiltering($enabled)
623
- {
624
- return $this->_plugin->SetGlobalOption('filter-internal-ip', $enabled);
625
  }
626
 
627
- public function GetMainClientIP()
628
- {
629
  $result = null;
630
- if ($this->IsMainIPFromProxy()) {
631
- // TODO the algorithm below just gets the first IP in the list...we might want to make this more intelligent somehow
632
  $result = $this->GetClientIPs();
633
- $result = reset($result);
634
- $result = isset($result[0]) ? $result[0] : null;
635
- } elseif (isset($_SERVER['REMOTE_ADDR'])) {
636
- $result = $this->NormalizeIP($_SERVER['REMOTE_ADDR']);
637
- if (!$this->ValidateIP($result)) {
638
- $result = "Error " . self::ERROR_CODE_INVALID_IP . ": Invalid IP Address";
639
  }
640
  }
641
  return $result;
642
  }
643
 
644
- public function GetClientIPs()
645
- {
646
  $ips = array();
647
- foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) {
648
- if (isset($_SERVER[$key])) {
649
- $ips[$key] = array();
650
- foreach (explode(',', $_SERVER[$key]) as $ip) {
651
- if ($this->ValidateIP($ip = $this->NormalizeIP($ip))) {
652
- $ips[$key][] = $ip;
653
  }
654
  }
655
  }
@@ -657,59 +694,54 @@ class WSAL_Settings
657
  return $ips;
658
  }
659
 
660
- protected function NormalizeIP($ip)
661
- {
662
- $ip = trim($ip);
663
- if (strpos($ip, ':') !== false && substr_count($ip, '.') == 3 && strpos($ip, '[') === false) {
664
- // IPv4 with a port (eg: 11.22.33.44:80)
665
- $ip = explode(':', $ip);
666
  $ip = $ip[0];
667
  } else {
668
- // IPv6 with a port (eg: [::1]:80)
669
- $ip = explode(']', $ip);
670
- $ip = ltrim($ip[0], '[');
671
  }
672
  return $ip;
673
  }
674
 
675
- protected function ValidateIP($ip)
676
- {
677
  $opts = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
678
- if ($this->IsInternalIPsFiltered()) {
679
  $opts = $opts | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
680
  }
681
- $filteredIP = filter_var($ip, FILTER_VALIDATE_IP, $opts);
682
- if (!$filteredIP || empty($filteredIP)) {
683
- //Regex IPV4
684
- if (preg_match("/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/", $ip)) {
685
  return $ip;
686
- }
687
- //Regex IPV6
688
- elseif (preg_match("/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/", $ip)) {
689
  return $ip;
690
  }
691
- if (!$this->IsLoggingDisabled) {
692
- error_log("Invalid IP in ValidateIP function: ".$ip);
693
  }
694
  return false;
695
  } else {
696
- return $filteredIP;
697
  }
698
  }
699
 
700
  /**
701
- * Users excluded from monitoring
702
  */
703
- public function SetExcludedMonitoringUsers($users)
704
- {
705
  $this->_excluded_users = $users;
706
- $this->_plugin->SetGlobalOption('excluded-users', esc_html(implode(',', $this->_excluded_users)));
707
  }
708
 
709
- public function GetExcludedMonitoringUsers()
710
- {
711
- if (empty($this->_excluded_users)) {
712
- $this->_excluded_users = array_unique(array_filter(explode(',', $this->_plugin->GetGlobalOption('excluded-users'))));
713
  }
714
  return $this->_excluded_users;
715
  }
@@ -738,53 +770,47 @@ class WSAL_Settings
738
  }
739
 
740
  /**
741
- * Roles excluded from monitoring
742
  */
743
- public function SetExcludedMonitoringRoles($roles)
744
- {
745
  $this->_excluded_roles = $roles;
746
- $this->_plugin->SetGlobalOption('excluded-roles', esc_html(implode(',', $this->_excluded_roles)));
747
  }
748
 
749
- public function GetExcludedMonitoringRoles()
750
- {
751
- if (empty($this->_excluded_roles)) {
752
- $this->_excluded_roles = array_unique(array_filter(explode(',', $this->_plugin->GetGlobalOption('excluded-roles'))));
753
  }
754
  return $this->_excluded_roles;
755
  }
756
 
757
  /**
758
- * Custom fields excluded from monitoring
759
  */
760
- public function SetExcludedMonitoringCustom($custom)
761
- {
762
  $this->_excluded_custom = $custom;
763
- $this->_plugin->SetGlobalOption('excluded-custom', esc_html(implode(',', $this->_excluded_custom)));
764
  }
765
 
766
- public function GetExcludedMonitoringCustom()
767
- {
768
- if (empty($this->_excluded_custom)) {
769
- $this->_excluded_custom = array_unique(array_filter(explode(',', $this->_plugin->GetGlobalOption('excluded-custom'))));
770
- asort($this->_excluded_custom);
771
  }
772
  return $this->_excluded_custom;
773
  }
774
 
775
  /**
776
- * IP excluded from monitoring
777
  */
778
- public function SetExcludedMonitoringIP($ip)
779
- {
780
  $this->_excluded_ip = $ip;
781
- $this->_plugin->SetGlobalOption('excluded-ip', esc_html(implode(',', $this->_excluded_ip)));
782
  }
783
 
784
- public function GetExcludedMonitoringIP()
785
- {
786
- if (empty($this->_excluded_ip)) {
787
- $this->_excluded_ip = array_unique(array_filter(explode(',', $this->_plugin->GetGlobalOption('excluded-ip'))));
788
  }
789
  return $this->_excluded_ip;
790
  }
@@ -792,16 +818,15 @@ class WSAL_Settings
792
  /**
793
  * Datetime used in the Alerts.
794
  */
795
- public function GetDatetimeFormat($lineBreak = true)
796
- {
797
- if ($lineBreak) {
798
  $date_time_format = $this->GetDateFormat() . '<\b\r>' . $this->GetTimeFormat();
799
  } else {
800
  $date_time_format = $this->GetDateFormat() . ' ' . $this->GetTimeFormat();
801
  }
802
 
803
- $wp_time_format = get_option('time_format');
804
- if (stripos($wp_time_format, 'A') !== false) {
805
  $date_time_format .= '.$$$&\n\b\s\p;A';
806
  } else {
807
  $date_time_format .= '.$$$';
@@ -812,43 +837,40 @@ class WSAL_Settings
812
  /**
813
  * Date Format from WordPress General Settings.
814
  */
815
- public function GetDateFormat()
816
- {
817
- $wp_date_format = get_option('date_format');
818
- $search = array('F', 'M', 'n', 'j', ' ', '/', 'y', 'S', ',', 'l', 'D');
819
- $replace = array('m', 'm', 'm', 'd', '-', '-', 'Y', '', '', '', '');
820
- $date_format = str_replace($search, $replace, $wp_date_format);
821
  return $date_format;
822
  }
823
 
824
  /**
825
  * Time Format from WordPress General Settings.
826
  */
827
- public function GetTimeFormat()
828
- {
829
- $wp_time_format = get_option('time_format');
830
- $search = array('a', 'A', 'T', ' ');
831
- $replace = array('', '', '', '');
832
- $time_format = str_replace($search, $replace, $wp_time_format);
833
  return $time_format;
834
  }
835
 
836
  /**
837
- * Alerts Timestamp
838
- * Server's timezone or WordPress' timezone
 
839
  */
840
- public function GetTimezone()
841
- {
842
- return $this->_plugin->GetGlobalOption('timezone', 0);
843
  }
844
 
845
- public function SetTimezone($newvalue)
846
- {
847
- return $this->_plugin->SetGlobalOption('timezone', $newvalue);
848
  }
849
 
850
  /**
851
- * Get type of username to display
852
  */
853
  public function get_type_username() {
854
  return $this->_plugin->GetGlobalOption( 'type_username', 'display_name' );
@@ -864,84 +886,89 @@ class WSAL_Settings
864
  return $this->_plugin->SetGlobalOption( 'type_username', $newvalue );
865
  }
866
 
867
- public function GetAdapterConfig($name_field)
868
- {
869
- return $this->_plugin->GetGlobalOption($name_field);
870
  }
871
 
872
- public function SetAdapterConfig($name_field, $newvalue)
873
- {
874
- return $this->_plugin->SetGlobalOption($name_field, trim($newvalue));
875
  }
876
 
877
- public function GetColumns()
878
- {
879
- $columns = array('alert_code' => '1', 'type' => '1', 'date' => '1', 'username' => '1', 'source_ip' => '1', 'message' => '1');
880
- if ($this->_plugin->IsMultisite()) {
881
- $columns = array_slice($columns, 0, 5, true) + array('site' => '1') + array_slice($columns, 5, null, true);
 
 
 
 
 
 
 
 
882
  }
883
  $selected = $this->GetColumnsSelected();
884
- if (!empty($selected)) {
885
- $columns = array('alert_code' => '0', 'type' => '0', 'date' => '0', 'username' => '0', 'source_ip' => '0', 'message' => '0');
886
- if ($this->_plugin->IsMultisite()) {
887
- $columns = array_slice($columns, 0, 5, true) + array('site' => '0') + array_slice($columns, 5, null, true);
 
 
 
 
 
 
 
 
 
888
  }
889
- $selected = (array)json_decode($selected);
890
- $columns = array_merge($columns, $selected);
891
  return $columns;
892
  } else {
893
  return $columns;
894
  }
895
  }
896
 
897
- public function GetColumnsSelected()
898
- {
899
- return $this->_plugin->GetGlobalOption('columns');
900
  }
901
 
902
- public function SetColumns($columns)
903
- {
904
- return $this->_plugin->SetGlobalOption('columns', json_encode($columns));
905
  }
906
 
907
- public function IsWPBackend()
908
- {
909
- return $this->_plugin->GetGlobalOption('wp-backend');
910
  }
911
 
912
- public function SetWPBackend($enabled)
913
- {
914
- return $this->_plugin->SetGlobalOption('wp-backend', $enabled);
915
  }
916
 
917
- public function SetFromEmail($email_address)
918
- {
919
- return $this->_plugin->SetGlobalOption('from-email', trim($email_address));
920
  }
921
 
922
- public function GetFromEmail()
923
- {
924
- return $this->_plugin->GetGlobalOption('from-email');
925
  }
926
 
927
- public function SetDisplayName($display_name)
928
- {
929
- return $this->_plugin->SetGlobalOption('display-name', trim($display_name));
930
  }
931
 
932
- public function GetDisplayName()
933
- {
934
- return $this->_plugin->GetGlobalOption('display-name');
935
  }
936
 
937
- public function Set404LogLimit($value)
938
- {
939
- return $this->_plugin->SetGlobalOption('log-404-limit', abs($value));
940
  }
941
 
942
- public function Get404LogLimit()
943
- {
944
- return $this->_plugin->GetGlobalOption('log-404-limit', 99);
945
  }
946
 
947
  /**
@@ -1009,28 +1036,23 @@ class WSAL_Settings
1009
  return $this->_plugin->GetGlobalOption( 'log-visitor-failed-login-limit', 10 );
1010
  }
1011
 
1012
- /*============================== Support Archive Database ==============================*/
1013
-
1014
- public function IsArchivingEnabled()
1015
- {
1016
- return $this->_plugin->GetGlobalOption('archiving-e');
1017
  }
1018
 
1019
  /**
1020
- * Switch to Archive DB if is enabled
1021
  */
1022
- public function SwitchToArchiveDB()
1023
- {
1024
- if ($this->IsArchivingEnabled()) {
1025
- $archiveType = $this->_plugin->GetGlobalOption('archive-type');
1026
- $archiveUser = $this->_plugin->GetGlobalOption('archive-user');
1027
- $password = $this->_plugin->GetGlobalOption('archive-password');
1028
- $archiveName = $this->_plugin->GetGlobalOption('archive-name');
1029
- $archiveHostname = $this->_plugin->GetGlobalOption('archive-hostname');
1030
- $archiveBasePrefix = $this->_plugin->GetGlobalOption('archive-base-prefix');
1031
- $config = WSAL_Connector_ConnectorFactory::GetConfigArray($archiveType, $archiveUser, $password, $archiveName, $archiveHostname, $archiveBasePrefix);
1032
- $this->_plugin->getConnector($config)->getAdapter('Occurrence');
1033
  }
1034
  }
1035
- // </editor-fold>
1036
  }
1
  <?php
2
  /**
3
+ * Class: WSAL Settings.
4
+ *
5
+ * WSAL settings class.
6
  *
7
+ * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
  * This class is the actual controller of the Settings Page.
17
+ *
18
+ * @package Wsal
19
  */
20
+ class WSAL_Settings {
21
+
22
  /**
23
+ * Instance of the main plugin.
24
+ *
25
  * @var WpSecurityAuditLog
26
  */
27
  protected $_plugin;
28
 
 
29
  const OPT_DEV_DATA_INSPECTOR = 'd';
30
  const OPT_DEV_PHP_ERRORS = 'p';
31
  const OPT_DEV_REQUEST_LOG = 'r';
33
 
34
  const ERROR_CODE_INVALID_IP = 901;
35
 
36
+ /**
37
+ * Dev Options.
38
+ *
39
+ * @var array
40
+ */
41
  protected $_devoption = null;
42
 
43
+ /**
44
+ * Pruning Date.
45
+ *
46
+ * @var string
47
+ */
48
  protected $_pruning = 0;
49
 
50
+ /**
51
+ * IDs of disabled alerts.
52
+ *
53
+ * @var array
54
+ */
55
  protected $_disabled = null;
56
 
57
+ /**
58
+ * Allowed Plugin Viewers.
59
+ *
60
+ * @var array
61
+ */
62
  protected $_viewers = null;
63
 
64
+ /**
65
+ * Allowed Plugin Editors.
66
+ *
67
+ * @var array
68
+ */
69
  protected $_editors = null;
70
 
71
+ /**
72
+ * Alerts per page.
73
+ *
74
+ * @var int
75
+ */
76
  protected $_perpage = null;
77
 
78
  /**
79
+ * Users excluded from monitoring.
80
+ *
81
+ * @var array
82
  */
83
  protected $_excluded_users = array();
84
 
85
  /**
86
+ * Roles excluded from monitoring.
87
+ *
88
+ * @var array
89
  */
90
+ protected $_excluded_roles = array();
91
 
92
  /**
93
+ * Custom fields excluded from monitoring.
94
+ *
95
+ * @var array
96
  */
97
  protected $_excluded_custom = array();
98
 
104
  protected $_post_types = array();
105
 
106
  /**
107
+ * IP excluded from monitoring.
108
+ *
109
+ * @var array
110
  */
111
  protected $_excluded_ip = array();
112
 
113
+ /**
114
+ * Method: Constructor.
115
+ *
116
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
117
+ */
118
+ public function __construct( WpSecurityAuditLog $plugin ) {
119
  $this->_plugin = $plugin;
120
  }
121
 
122
  /**
123
+ * Return array of developer options to be enabled by default.
124
+ *
125
+ * @return array
126
  */
127
+ public function GetDefaultDevOptions() {
 
128
  return array();
129
  }
130
 
131
  /**
132
  * Returns whether a developer option is enabled or not.
133
+ *
134
+ * @param string $option - See self::OPT_DEV_* constants.
135
+ * @return boolean - If option is enabled or not.
136
  */
137
+ public function IsDevOptionEnabled( $option ) {
138
+ if ( is_null( $this->_devoption ) ) {
 
139
  $this->_devoption = $this->_plugin->GetGlobalOption(
140
  'dev-options',
141
+ implode( ',', $this->GetDefaultDevOptions() )
142
  );
143
+ $this->_devoption = explode( ',', $this->_devoption );
144
  }
145
+ return in_array( $option, $this->_devoption );
146
  }
147
 
148
  /**
149
+ * Check whether any developer option has been enabled or not.
150
+ *
151
+ * @return boolean
152
  */
153
+ public function IsAnyDevOptionEnabled() {
154
+ return ! ! $this->_plugin->GetGlobalOption( 'dev-options', null );
 
155
  }
156
 
157
  /**
158
  * Sets whether a developer option is enabled or not.
159
+ *
160
+ * @param string $option - See self::OPT_DEV_* constants.
161
+ * @param boolean $enabled - If option should be enabled or not.
162
  */
163
+ public function SetDevOptionEnabled( $option, $enabled ) {
164
+ // Make sure options have been loaded.
165
+ $this->IsDevOptionEnabled( '' );
166
+ // Remove option if it exists.
167
+ while ( ($p = array_search( $option, $this->_devoption )) !== false ) {
168
+ unset( $this->_devoption[ $p ] );
 
169
  }
170
+ // Add option if callee wants it enabled.
171
+ if ( $enabled ) {
172
  $this->_devoption[] = $option;
173
  }
174
+ // Commit option.
175
  $this->_plugin->SetGlobalOption(
176
  'dev-options',
177
+ implode( ',', $this->_devoption )
178
  );
179
  }
180
 
181
  /**
182
  * Remove all enabled developer options.
183
  */
184
+ public function ClearDevOptions() {
 
185
  $this->_devoption = array();
186
+ $this->_plugin->SetGlobalOption( 'dev-options', '' );
187
  }
188
 
189
  /**
190
+ * Check whether to enable data inspector or not.
191
+ *
192
+ * @return boolean
193
  */
194
+ public function IsDataInspectorEnabled() {
195
+ return $this->IsDevOptionEnabled( self::OPT_DEV_DATA_INSPECTOR );
 
196
  }
197
 
198
  /**
199
+ * Check whether to enable PHP error logging or not.
200
+ *
201
+ * @return boolean
202
  */
203
+ public function IsPhpErrorLoggingEnabled() {
204
+ return $this->IsDevOptionEnabled( self::OPT_DEV_PHP_ERRORS );
 
205
  }
206
 
207
  /**
208
+ * Check whether to log requests to file or not.
209
+ *
210
+ * @return boolean
211
  */
212
+ public function IsRequestLoggingEnabled() {
213
+ return $this->IsDevOptionEnabled( self::OPT_DEV_REQUEST_LOG );
 
214
  }
215
 
216
  /**
217
+ * Check whether to store debug backtrace for PHP alerts or not.
218
+ *
219
+ * @return boolean
220
  */
221
+ public function IsBacktraceLoggingEnabled() {
222
+ return $this->IsDevOptionEnabled( self::OPT_DEV_BACKTRACE_LOG );
 
223
  }
224
 
 
 
225
  /**
226
+ * Check whether dashboard widgets are enabled or not.
227
+ *
228
+ * @return boolean
229
  */
230
+ public function IsWidgetsEnabled() {
231
+ return ! $this->_plugin->GetGlobalOption( 'disable-widgets' );
 
232
  }
233
 
234
  /**
235
+ * Check whether dashboard widgets are enabled or not.
236
+ *
237
+ * @param boolean $newvalue - True if enabled.
238
  */
239
+ public function SetWidgetsEnabled( $newvalue ) {
240
+ $this->_plugin->SetGlobalOption( 'disable-widgets', ! $newvalue );
 
241
  }
242
 
243
  /**
244
+ * Check whether alerts in audit log view refresh automatically or not.
245
+ *
246
+ * @return boolean
247
  */
248
+ public function IsRefreshAlertsEnabled() {
249
+ return ! $this->_plugin->GetGlobalOption( 'disable-refresh' );
 
250
  }
251
 
252
  /**
253
+ * Check whether alerts in audit log view refresh automatically or not.
254
+ *
255
+ * @param boolean $newvalue - True if enabled.
256
  */
257
+ public function SetRefreshAlertsEnabled( $newvalue ) {
258
+ $this->_plugin->SetGlobalOption( 'disable-refresh', ! $newvalue );
 
259
  }
260
 
261
  /**
262
+ * Maximum number of alerts to show in dashboard widget.
263
+ *
264
+ * @return int
265
  */
266
+ public function GetDashboardWidgetMaxAlerts() {
 
267
  return 5;
268
  }
269
 
 
270
  /**
271
+ * The maximum number of alerts allowable.
272
+ *
273
+ * @return int
274
  */
275
+ public function GetMaxAllowedAlerts() {
 
276
  return 5000;
277
  }
278
 
279
  /**
280
+ * The default pruning date.
281
+ *
282
+ * @return string
283
  */
284
+ public function GetDefaultPruningDate() {
285
+ return '12 months';
 
286
  }
287
 
288
  /**
289
+ * The current pruning date.
290
+ *
291
+ * @return string
292
  */
293
+ public function GetPruningDate() {
294
+ if ( ! $this->_pruning ) {
295
+ $this->_pruning = $this->_plugin->GetGlobalOption( 'pruning-date' );
296
+ if ( ! strtotime( $this->_pruning ) ) {
 
297
  $this->_pruning = $this->GetDefaultPruningDate();
298
  }
299
  }
301
  }
302
 
303
  /**
304
+ * Set the new pruning date.
305
+ *
306
+ * @param string $newvalue - The new pruning date.
307
  */
308
+ public function SetPruningDate( $newvalue ) {
309
+ if ( strtotime( $newvalue ) ) {
310
+ $this->_plugin->SetGlobalOption( 'pruning-date', $newvalue );
 
311
  $this->_pruning = $newvalue;
312
  }
313
  }
314
 
315
  /**
316
+ * Maximum number of alerts to keep.
317
+ *
318
+ * @return integer
319
  */
320
+ public function GetPruningLimit() {
321
+ $val = (int) $this->_plugin->GetGlobalOption( 'pruning-limit' );
 
322
  return $val ? $val : $this->GetMaxAllowedAlerts();
323
  }
324
 
325
  /**
326
+ * Set pruning alerts limit.
327
+ *
328
+ * @param integer $newvalue - The new maximum number of alerts.
329
  */
330
+ public function SetPruningLimit( $newvalue ) {
331
+ $newvalue = max( /*min(*/ (int) $newvalue/*, $this->GetMaxAllowedAlerts())*/, 1 );
332
+ $this->_plugin->SetGlobalOption( 'pruning-limit', $newvalue );
 
333
  }
334
 
335
+ public function SetPruningDateEnabled( $enabled ) {
336
+ $this->_plugin->SetGlobalOption( 'pruning-date-e', $enabled );
 
337
  }
338
 
339
+ public function SetPruningLimitEnabled( $enabled ) {
340
+ $this->_plugin->SetGlobalOption( 'pruning-limit-e', $enabled );
 
341
  }
342
 
343
+ public function IsPruningDateEnabled() {
344
+ return $this->_plugin->GetGlobalOption( 'pruning-date-e' );
345
  }
346
 
347
+ public function IsPruningLimitEnabled() {
348
+ return $this->_plugin->GetGlobalOption( 'pruning-limit-e' );
 
349
  }
350
 
351
+ public function IsRestrictAdmins() {
352
+ return $this->_plugin->GetGlobalOption( 'restrict-admins', false );
 
353
  }
354
 
355
  /**
356
+ * Sandbox functionality is now in an external plugin.
357
+ *
358
+ * @deprecated
359
  */
360
+ public function IsSandboxPageEnabled() {
361
+ // $plugins = $this->_plugin->licensing->plugins();
362
+ // return isset($plugins['wsal-sandbox-extensionphp']);
363
+ return esc_html__( 'This function is deprecated', 'wp-security-audit-log' );
364
  }
365
 
366
+ public function SetRestrictAdmins( $enable ) {
367
+ $this->_plugin->SetGlobalOption( 'restrict-admins', (bool) $enable );
 
368
  }
369
 
370
+ public function GetDefaultDisabledAlerts() {
371
+ return array( 0000, 0001, 0002, 0003, 0004, 0005 );
 
 
372
  }
373
 
374
  /**
375
+ * Return IDs of disabled alerts.
376
+ *
377
+ * @return array
378
  */
379
+ public function GetDisabledAlerts() {
380
+ if ( ! $this->_disabled ) {
381
+ $this->_disabled = implode( ',', $this->GetDefaultDisabledAlerts() );
382
+ $this->_disabled = $this->_plugin->GetGlobalOption( 'disabled-alerts', $this->_disabled );
383
+ $this->_disabled = ( '' == $this->_disabled ) ? array() : explode( ',', $this->_disabled );
384
+ $this->_disabled = array_map( 'intval', $this->_disabled );
 
385
  }
386
  return $this->_disabled;
387
  }
388
 
389
  /**
390
+ * Method: Set Disabled Alerts.
391
+ *
392
  * @param array $types IDs alerts to disable.
393
  */
394
+ public function SetDisabledAlerts( $types ) {
395
+ $this->_disabled = array_unique( array_map( 'intval', $types ) );
396
+ $this->_plugin->SetGlobalOption( 'disabled-alerts', implode( ',', $this->_disabled ) );
 
397
  }
398
 
399
+ public function IsIncognito() {
400
+ return $this->_plugin->GetGlobalOption( 'hide-plugin' );
 
401
  }
402
 
403
+ public function SetIncognito( $enabled ) {
404
+ return $this->_plugin->SetGlobalOption( 'hide-plugin', $enabled );
 
405
  }
406
 
407
  /**
408
  * Checking if Logging is enabled.
409
  */
410
+ public function IsLoggingDisabled() {
411
+ return $this->_plugin->GetGlobalOption( 'disable-logging' );
 
412
  }
413
 
414
+ public function SetLoggingDisabled( $disabled ) {
415
+ return $this->_plugin->SetGlobalOption( 'disable-logging', $disabled );
 
416
  }
417
 
418
  /**
419
  * Checking if the data will be removed.
420
  */
421
+ public function IsDeleteData() {
422
+ return $this->_plugin->GetGlobalOption( 'delete-data' );
 
423
  }
424
 
425
+ public function SetDeleteData( $enabled ) {
426
+ return $this->_plugin->SetGlobalOption( 'delete-data', $enabled );
 
427
  }
428
 
429
+ public function SetAllowedPluginViewers( $users_or_roles ) {
430
+ $this->_viewers = $users_or_roles;
431
+ $this->_plugin->SetGlobalOption( 'plugin-viewers', implode( ',', $this->_viewers ) );
 
 
 
432
  }
433
 
434
+ public function GetAllowedPluginViewers() {
435
+ if ( is_null( $this->_viewers ) ) {
436
+ $this->_viewers = array_unique( array_filter( explode( ',', $this->_plugin->GetGlobalOption( 'plugin-viewers' ) ) ) );
 
437
  }
438
  return $this->_viewers;
439
  }
440
 
441
+ public function SetAllowedPluginEditors( $users_or_roles ) {
442
+ $this->_editors = $users_or_roles;
443
+ $this->_plugin->SetGlobalOption( 'plugin-editors', implode( ',', $this->_editors ) );
 
444
  }
445
 
446
+ public function GetAllowedPluginEditors() {
447
+ if ( is_null( $this->_editors ) ) {
448
+ $this->_editors = array_unique( array_filter( explode( ',', $this->_plugin->GetGlobalOption( 'plugin-editors' ) ) ) );
 
449
  }
450
  return $this->_editors;
451
  }
452
 
453
+ public function SetViewPerPage( $newvalue ) {
454
+ $this->_perpage = max( $newvalue, 1 );
455
+ $this->_plugin->SetGlobalOption( 'items-per-page', $this->_perpage );
 
456
  }
457
 
458
+ public function GetViewPerPage() {
459
+ if ( is_null( $this->_perpage ) ) {
460
+ $this->_perpage = (int) $this->_plugin->GetGlobalOption( 'items-per-page', 10 );
 
461
  }
462
  return $this->_perpage;
463
  }
464
 
465
  /**
466
+ * Check if current user can perform an action.
467
+ *
468
  * @param string $action Type of action, either 'view' or 'edit'.
469
  * @return boolean If user has access or not.
470
  */
471
+ public function CurrentUserCan( $action ) {
472
+ return $this->UserCan( wp_get_current_user(), $action );
 
473
  }
474
 
475
  /**
476
+ * Get list of superadmin usernames.
477
+ *
478
+ * @return array
479
  */
480
+ protected function GetSuperAdmins() {
 
481
  return $this->_plugin->IsMultisite() ? get_super_admins() : array();
482
  }
483
 
484
  /**
485
+ * List of admin usernames.
486
+ *
487
+ * @return string[]
488
  */
489
+ protected function GetAdmins() {
490
+ if ( $this->_plugin->IsMultisite() ) {
491
+ /**
492
+ * Get list of admins.
493
+ *
494
+ * @see https://gist.github.com/1508426/65785a15b8638d43a9905effb59e4d97319ef8f8
495
+ */
496
  global $wpdb;
497
+ $cap = $wpdb->prefix . 'capabilities';
498
  $sql = "SELECT DISTINCT $wpdb->users.user_login"
499
  . " FROM $wpdb->users"
500
  . " INNER JOIN $wpdb->usermeta ON ($wpdb->users.ID = $wpdb->usermeta.user_id )"
501
  . " WHERE $wpdb->usermeta.meta_key = '$cap'"
502
  . " AND CAST($wpdb->usermeta.meta_value AS CHAR) LIKE '%\"administrator\"%'";
503
+ return $wpdb->get_col( $sql );
504
  } else {
505
  $result = array();
506
  $query = 'role=administrator&fields[]=user_login';
507
+ foreach ( get_users( $query ) as $user ) {
508
  $result[] = $user->user_login;
509
  }
510
  return $result;
513
 
514
  /**
515
  * Returns access tokens for a particular action.
516
+ *
517
+ * @param string $action - Type of action.
518
  * @return string[] List of tokens (usernames, roles etc).
519
  */
520
+ public function GetAccessTokens( $action ) {
 
521
  $allowed = array();
522
+ switch ( $action ) {
523
  case 'view':
524
  $allowed = $this->GetAllowedPluginViewers();
525
+ $allowed = array_merge( $allowed, $this->GetAllowedPluginEditors() );
526
+ if ( ! $this->IsRestrictAdmins() ) {
527
+ $allowed = array_merge( $allowed, $this->GetSuperAdmins() );
528
+ $allowed = array_merge( $allowed, $this->GetAdmins() );
529
  }
530
  break;
531
  case 'edit':
532
  $allowed = $this->GetAllowedPluginEditors();
533
+ if ( ! $this->IsRestrictAdmins() ) {
534
+ $allowed = array_merge( $allowed, $this->_plugin->IsMultisite() ? $this->GetSuperAdmins() : $this->GetAdmins() );
535
  }
536
  break;
537
  default:
538
+ throw new Exception( 'Unknown action "' . $action . '".' );
539
  }
540
+ if ( ! $this->IsRestrictAdmins() ) {
541
+ if ( is_multisite() ) {
542
+ $allowed = array_merge( $allowed, get_super_admins() );
543
  } else {
544
  $allowed[] = 'administrator';
545
  }
546
  }
547
+ return array_unique( $allowed );
548
  }
549
 
550
  /**
551
+ * Check if user can perform an action.
552
+ *
553
+ * @param integer|WP_user $user - User object to check.
554
+ * @param string $action - Type of action, either 'view' or 'edit'.
555
  * @return boolean If user has access or not.
556
  */
557
+ public function UserCan( $user, $action ) {
558
+ if ( is_int( $user ) ) {
559
+ $user = get_userdata( $user );
 
560
  }
561
+ $allowed = $this->GetAccessTokens( $action );
562
+ $check = array_merge( $user->roles, array( $user->user_login ) );
563
+ foreach ( $check as $item ) {
564
+ if ( in_array( $item, $allowed ) ) {
565
  return true;
566
  }
567
  }
568
  return false;
569
  }
570
 
571
+ public function GetCurrentUserRoles( $base_roles = null ) {
572
+ if ( null == $base_roles ) {
573
+ $base_roles = wp_get_current_user()->roles;
 
574
  }
575
+ if ( function_exists( 'is_super_admin' ) && is_super_admin() ) {
576
+ $base_roles[] = 'superadmin';
577
  }
578
+ return $base_roles;
579
  }
580
 
581
+ public function IsLoginSuperAdmin( $username ) {
582
+ $user_id = username_exists( $username );
583
+ if ( function_exists( 'is_super_admin' ) && is_super_admin( $user_id ) ) {
 
584
  return true;
585
  } else {
586
  return false;
587
  }
588
  }
589
 
590
+ public function GetLicenses() {
591
+ return $this->_plugin->GetGlobalOption( 'licenses' );
 
 
 
 
592
  }
593
 
594
+ public function GetLicense( $name ) {
 
595
  $data = $this->GetLicenses();
596
+ $name = sanitize_key( basename( $name ) );
597
+ return isset( $data[ $name ] ) ? $data[ $name ] : array();
598
  }
599
 
600
+ public function SetLicenses( $data ) {
601
+ $this->_plugin->SetGlobalOption( 'licenses', $data );
 
602
  }
603
 
604
+ public function GetLicenseKey( $name ) {
605
+ $data = $this->GetLicense( $name );
606
+ return isset( $data['key'] ) ? $data['key'] : '';
 
607
  }
608
 
609
+ public function GetLicenseStatus( $name ) {
610
+ $data = $this->GetLicense( $name );
611
+ return isset( $data['sts'] ) ? $data['sts'] : '';
612
  }
613
 
614
+ public function GetLicenseErrors( $name ) {
615
+ $data = $this->GetLicense( $name );
616
+ return isset( $data['err'] ) ? $data['err'] : '';
 
617
  }
618
 
619
+ public function SetLicenseKey( $name, $key ) {
 
620
  $data = $this->GetLicenses();
621
+ if ( ! isset( $data[ $name ] ) ) {
622
+ $data[ $name ] = array();
623
  }
624
+ $data[ $name ]['key'] = $key;
625
+ $this->SetLicenses( $data );
626
  }
627
 
628
+ public function SetLicenseStatus( $name, $status ) {
 
629
  $data = $this->GetLicenses();
630
+ if ( ! isset( $data[ $name ] ) ) {
631
+ $data[ $name ] = array();
632
  }
633
+ $data[ $name ]['sts'] = $status;
634
+ $this->SetLicenses( $data );
635
  }
636
 
637
+ public function SetLicenseErrors( $name, $errors ) {
 
638
  $data = $this->GetLicenses();
639
+ if ( ! isset( $data[ $name ] ) ) {
640
+ $data[ $name ] = array();
641
  }
642
+ $data[ $name ]['err'] = $errors;
643
+ $this->SetLicenses( $data );
644
  }
645
 
646
+ public function ClearLicenses() {
647
+ $this->SetLicenses( array() );
 
648
  }
649
 
650
+ public function IsMainIPFromProxy() {
651
+ return $this->_plugin->GetGlobalOption( 'use-proxy-ip' );
 
 
 
 
652
  }
653
 
654
+ public function SetMainIPFromProxy( $enabled ) {
655
+ return $this->_plugin->SetGlobalOption( 'use-proxy-ip', $enabled );
 
656
  }
657
 
658
+ public function IsInternalIPsFiltered() {
659
+ return $this->_plugin->GetGlobalOption( 'filter-internal-ip' );
 
660
  }
661
 
662
+ public function SetInternalIPsFiltering( $enabled ) {
663
+ return $this->_plugin->SetGlobalOption( 'filter-internal-ip', $enabled );
 
664
  }
665
 
666
+ public function GetMainClientIP() {
 
667
  $result = null;
668
+ if ( $this->IsMainIPFromProxy() ) {
669
+ // TODO: The algorithm below just gets the first IP in the list...we might want to make this more intelligent somehow.
670
  $result = $this->GetClientIPs();
671
+ $result = reset( $result );
672
+ $result = isset( $result[0] ) ? $result[0] : null;
673
+ } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
674
+ $result = $this->NormalizeIP( $_SERVER['REMOTE_ADDR'] );
675
+ if ( ! $this->ValidateIP( $result ) ) {
676
+ $result = 'Error ' . self::ERROR_CODE_INVALID_IP . ': Invalid IP Address';
677
  }
678
  }
679
  return $result;
680
  }
681
 
682
+ public function GetClientIPs() {
 
683
  $ips = array();
684
+ foreach ( array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ) as $key ) {
685
+ if ( isset( $_SERVER[ $key ] ) ) {
686
+ $ips[ $key ] = array();
687
+ foreach ( explode( ',', $_SERVER[ $key ] ) as $ip ) {
688
+ if ( $this->ValidateIP( $ip = $this->NormalizeIP( $ip ) ) ) {
689
+ $ips[ $key ][] = $ip;
690
  }
691
  }
692
  }
694
  return $ips;
695
  }
696
 
697
+ protected function NormalizeIP( $ip ) {
698
+ $ip = trim( $ip );
699
+ if ( strpos( $ip, ':' ) !== false && substr_count( $ip, '.' ) == 3 && strpos( $ip, '[' ) === false ) {
700
+ // IPv4 with a port (eg: 11.22.33.44:80).
701
+ $ip = explode( ':', $ip );
 
702
  $ip = $ip[0];
703
  } else {
704
+ // IPv6 with a port (eg: [::1]:80).
705
+ $ip = explode( ']', $ip );
706
+ $ip = ltrim( $ip[0], '[' );
707
  }
708
  return $ip;
709
  }
710
 
711
+ protected function ValidateIP( $ip ) {
 
712
  $opts = FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6;
713
+ if ( $this->IsInternalIPsFiltered() ) {
714
  $opts = $opts | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
715
  }
716
+ $filtered_ip = filter_var( $ip, FILTER_VALIDATE_IP, $opts );
717
+ if ( ! $filtered_ip || empty( $filtered_ip ) ) {
718
+ // Regex IPV4.
719
+ if ( preg_match( '/^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $ip ) ) {
720
  return $ip;
721
+ } // Regex IPV6.
722
+ elseif ( preg_match( '/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/', $ip ) ) {
 
723
  return $ip;
724
  }
725
+ if ( ! $this->IsLoggingDisabled ) {
726
+ error_log( 'Invalid IP in ValidateIP function: ' . $ip );
727
  }
728
  return false;
729
  } else {
730
+ return $filtered_ip;
731
  }
732
  }
733
 
734
  /**
735
+ * Users excluded from monitoring.
736
  */
737
+ public function SetExcludedMonitoringUsers( $users ) {
 
738
  $this->_excluded_users = $users;
739
+ $this->_plugin->SetGlobalOption( 'excluded-users', esc_html( implode( ',', $this->_excluded_users ) ) );
740
  }
741
 
742
+ public function GetExcludedMonitoringUsers() {
743
+ if ( empty( $this->_excluded_users ) ) {
744
+ $this->_excluded_users = array_unique( array_filter( explode( ',', $this->_plugin->GetGlobalOption( 'excluded-users' ) ) ) );
 
745
  }
746
  return $this->_excluded_users;
747
  }
770
  }
771
 
772
  /**
773
+ * Roles excluded from monitoring.
774
  */
775
+ public function SetExcludedMonitoringRoles( $roles ) {
 
776
  $this->_excluded_roles = $roles;
777
+ $this->_plugin->SetGlobalOption( 'excluded-roles', esc_html( implode( ',', $this->_excluded_roles ) ) );
778
  }
779
 
780
+ public function GetExcludedMonitoringRoles() {
781
+ if ( empty( $this->_excluded_roles ) ) {
782
+ $this->_excluded_roles = array_unique( array_filter( explode( ',', $this->_plugin->GetGlobalOption( 'excluded-roles' ) ) ) );
 
783
  }
784
  return $this->_excluded_roles;
785
  }
786
 
787
  /**
788
+ * Custom fields excluded from monitoring.
789
  */
790
+ public function SetExcludedMonitoringCustom( $custom ) {
 
791
  $this->_excluded_custom = $custom;
792
+ $this->_plugin->SetGlobalOption( 'excluded-custom', esc_html( implode( ',', $this->_excluded_custom ) ) );
793
  }
794
 
795
+ public function GetExcludedMonitoringCustom() {
796
+ if ( empty( $this->_excluded_custom ) ) {
797
+ $this->_excluded_custom = array_unique( array_filter( explode( ',', $this->_plugin->GetGlobalOption( 'excluded-custom' ) ) ) );
798
+ asort( $this->_excluded_custom );
 
799
  }
800
  return $this->_excluded_custom;
801
  }
802
 
803
  /**
804
+ * IP excluded from monitoring.
805
  */
806
+ public function SetExcludedMonitoringIP( $ip ) {
 
807
  $this->_excluded_ip = $ip;
808
+ $this->_plugin->SetGlobalOption( 'excluded-ip', esc_html( implode( ',', $this->_excluded_ip ) ) );
809
  }
810
 
811
+ public function GetExcludedMonitoringIP() {
812
+ if ( empty( $this->_excluded_ip ) ) {
813
+ $this->_excluded_ip = array_unique( array_filter( explode( ',', $this->_plugin->GetGlobalOption( 'excluded-ip' ) ) ) );
 
814
  }
815
  return $this->_excluded_ip;
816
  }
818
  /**
819
  * Datetime used in the Alerts.
820
  */
821
+ public function GetDatetimeFormat( $line_break = true ) {
822
+ if ( $line_break ) {
 
823
  $date_time_format = $this->GetDateFormat() . '<\b\r>' . $this->GetTimeFormat();
824
  } else {
825
  $date_time_format = $this->GetDateFormat() . ' ' . $this->GetTimeFormat();
826
  }
827
 
828
+ $wp_time_format = get_option( 'time_format' );
829
+ if ( stripos( $wp_time_format, 'A' ) !== false ) {
830
  $date_time_format .= '.$$$&\n\b\s\p;A';
831
  } else {
832
  $date_time_format .= '.$$$';
837
  /**
838
  * Date Format from WordPress General Settings.
839
  */
840
+ public function GetDateFormat() {
841
+ $wp_date_format = get_option( 'date_format' );
842
+ $search = array( 'F', 'M', 'n', 'j', ' ', '/', 'y', 'S', ',', 'l', 'D' );
843
+ $replace = array( 'm', 'm', 'm', 'd', '-', '-', 'Y', '', '', '', '' );
844
+ $date_format = str_replace( $search, $replace, $wp_date_format );
 
845
  return $date_format;
846
  }
847
 
848
  /**
849
  * Time Format from WordPress General Settings.
850
  */
851
+ public function GetTimeFormat() {
852
+ $wp_time_format = get_option( 'time_format' );
853
+ $search = array( 'a', 'A', 'T', ' ' );
854
+ $replace = array( '', '', '', '' );
855
+ $time_format = str_replace( $search, $replace, $wp_time_format );
 
856
  return $time_format;
857
  }
858
 
859
  /**
860
+ * Alerts Timestamp.
861
+ *
862
+ * Server's timezone or WordPress' timezone.
863
  */
864
+ public function GetTimezone() {
865
+ return $this->_plugin->GetGlobalOption( 'timezone', 0 );
 
866
  }
867
 
868
+ public function SetTimezone( $newvalue ) {
869
+ return $this->_plugin->SetGlobalOption( 'timezone', $newvalue );
 
870
  }
871
 
872
  /**
873
+ * Get type of username to display.
874
  */
875
  public function get_type_username() {
876
  return $this->_plugin->GetGlobalOption( 'type_username', 'display_name' );
886
  return $this->_plugin->SetGlobalOption( 'type_username', $newvalue );
887
  }
888
 
889
+ public function GetAdapterConfig( $name_field ) {
890
+ return $this->_plugin->GetGlobalOption( $name_field );
 
891
  }
892
 
893
+ public function SetAdapterConfig( $name_field, $newvalue ) {
894
+ return $this->_plugin->SetGlobalOption( $name_field, trim( $newvalue ) );
 
895
  }
896
 
897
+ public function GetColumns() {
898
+ $columns = array(
899
+ 'alert_code' => '1',
900
+ 'type' => '1',
901
+ 'date' => '1',
902
+ 'username' => '1',
903
+ 'source_ip' => '1',
904
+ 'message' => '1',
905
+ );
906
+ if ( $this->_plugin->IsMultisite() ) {
907
+ $columns = array_slice( $columns, 0, 5, true ) + array(
908
+ 'site' => '1',
909
+ ) + array_slice( $columns, 5, null, true );
910
  }
911
  $selected = $this->GetColumnsSelected();
912
+ if ( ! empty( $selected ) ) {
913
+ $columns = array(
914
+ 'alert_code' => '0',
915
+ 'type' => '0',
916
+ 'date' => '0',
917
+ 'username' => '0',
918
+ 'source_ip' => '0',
919
+ 'message' => '0',
920
+ );
921
+ if ( $this->_plugin->IsMultisite() ) {
922
+ $columns = array_slice( $columns, 0, 5, true ) + array(
923
+ 'site' => '0',
924
+ ) + array_slice( $columns, 5, null, true );
925
  }
926
+ $selected = (array) json_decode( $selected );
927
+ $columns = array_merge( $columns, $selected );
928
  return $columns;
929
  } else {
930
  return $columns;
931
  }
932
  }
933
 
934
+ public function GetColumnsSelected() {
935
+ return $this->_plugin->GetGlobalOption( 'columns' );
 
936
  }
937
 
938
+ public function SetColumns( $columns ) {
939
+ return $this->_plugin->SetGlobalOption( 'columns', json_encode( $columns ) );
 
940
  }
941
 
942
+ public function IsWPBackend() {
943
+ return $this->_plugin->GetGlobalOption( 'wp-backend' );
 
944
  }
945
 
946
+ public function SetWPBackend( $enabled ) {
947
+ return $this->_plugin->SetGlobalOption( 'wp-backend', $enabled );
 
948
  }
949
 
950
+ public function SetFromEmail( $email_address ) {
951
+ return $this->_plugin->SetGlobalOption( 'from-email', trim( $email_address ) );
 
952
  }
953
 
954
+ public function GetFromEmail() {
955
+ return $this->_plugin->GetGlobalOption( 'from-email' );
 
956
  }
957
 
958
+ public function SetDisplayName( $display_name ) {
959
+ return $this->_plugin->SetGlobalOption( 'display-name', trim( $display_name ) );
 
960
  }
961
 
962
+ public function GetDisplayName() {
963
+ return $this->_plugin->GetGlobalOption( 'display-name' );
 
964
  }
965
 
966
+ public function Set404LogLimit( $value ) {
967
+ return $this->_plugin->SetGlobalOption( 'log-404-limit', abs( $value ) );
 
968
  }
969
 
970
+ public function Get404LogLimit() {
971
+ return $this->_plugin->GetGlobalOption( 'log-404-limit', 99 );
 
972
  }
973
 
974
  /**
1036
  return $this->_plugin->GetGlobalOption( 'log-visitor-failed-login-limit', 10 );
1037
  }
1038
 
1039
+ public function IsArchivingEnabled() {
1040
+ return $this->_plugin->GetGlobalOption( 'archiving-e' );
 
 
 
1041
  }
1042
 
1043
  /**
1044
+ * Switch to Archive DB if is enabled.
1045
  */
1046
+ public function SwitchToArchiveDB() {
1047
+ if ( $this->IsArchivingEnabled() ) {
1048
+ $archive_type = $this->_plugin->GetGlobalOption( 'archive-type' );
1049
+ $archive_user = $this->_plugin->GetGlobalOption( 'archive-user' );
1050
+ $password = $this->_plugin->GetGlobalOption( 'archive-password' );
1051
+ $archive_name = $this->_plugin->GetGlobalOption( 'archive-name' );
1052
+ $archive_hostname = $this->_plugin->GetGlobalOption( 'archive-hostname' );
1053
+ $archive_baseprefix = $this->_plugin->GetGlobalOption( 'archive-base-prefix' );
1054
+ $config = WSAL_Connector_ConnectorFactory::GetConfigArray( $archive_type, $archive_user, $password, $archive_name, $archive_hostname, $archive_baseprefix );
1055
+ $this->_plugin->getConnector( $config )->getAdapter( 'Occurrence' );
 
1056
  }
1057
  }
 
1058
  }
classes/SimpleProfiler.php CHANGED
@@ -2,53 +2,47 @@
2
  /**
3
  * @package Wsal
4
  */
5
- class WSAL_SimpleProfiler
6
- {
7
- protected $_items = array();
8
-
9
- public function Start($name)
10
- {
11
- $item = new WSAL_SimpleProfiler_Item($name);
12
- $this->_items[] = $item;
13
- return $item;
14
- }
15
-
16
- public function AsComment()
17
- {
18
- echo '<!-- ' . PHP_EOL;
19
- foreach ($this->_items as $item) {
20
- echo ' ' . $item . PHP_EOL;
21
- }
22
- echo '-->' . PHP_EOL;
23
- }
24
-
25
- public function GetItems()
26
- {
27
- return $this->_items;
28
- }
29
  }
30
 
31
- class WSAL_SimpleProfiler_Item
32
- {
33
- public function __construct($name)
34
- {
35
- $this->name = $name;
36
- $this->t_bgn = microtime(true);
37
- $this->m_bgn = memory_get_usage();
38
- }
39
-
40
- public function Stop()
41
- {
42
- $this->t_end = microtime(true);
43
- $this->m_end = memory_get_usage();
44
- }
45
-
46
- public function __toString()
47
- {
48
- $t_diff = $this->t_end - $this->t_bgn;
49
- $m_diff = $this->m_end - $this->m_bgn;
50
- return number_format($t_diff, 6) . 's '
51
- . str_pad(number_format($m_diff, 0), 12, ' ', STR_PAD_LEFT) . 'b '
52
- . $this->name;
53
- }
54
  }
2
  /**
3
  * @package Wsal
4
  */
5
+ class WSAL_SimpleProfiler {
6
+
7
+ protected $_items = array();
8
+
9
+ public function Start( $name ) {
10
+ $item = new WSAL_SimpleProfiler_Item( $name );
11
+ $this->_items[] = $item;
12
+ return $item;
13
+ }
14
+
15
+ public function AsComment() {
16
+ echo '<!-- ' . esc_html( PHP_EOL );
17
+ foreach ( $this->_items as $item ) {
18
+ echo ' ' . esc_html( $item . PHP_EOL );
19
+ }
20
+ echo '-->' . esc_html( PHP_EOL );
21
+ }
22
+
23
+ public function GetItems() {
24
+ return $this->_items;
25
+ }
 
 
 
26
  }
27
 
28
+ class WSAL_SimpleProfiler_Item {
29
+
30
+ public function __construct( $name ) {
31
+ $this->name = $name;
32
+ $this->t_bgn = microtime( true );
33
+ $this->m_bgn = memory_get_usage();
34
+ }
35
+
36
+ public function Stop() {
37
+ $this->t_end = microtime( true );
38
+ $this->m_end = memory_get_usage();
39
+ }
40
+
41
+ public function __toString() {
42
+ $t_diff = $this->t_end - $this->t_bgn;
43
+ $m_diff = $this->m_end - $this->m_bgn;
44
+ return number_format( $t_diff, 6 ) . 's '
45
+ . str_pad( number_format( $m_diff, 0 ), 12, ' ', STR_PAD_LEFT ) . 'b '
46
+ . $this->name;
47
+ }
 
 
 
48
  }
classes/ViewManager.php CHANGED
@@ -1,290 +1,316 @@
1
  <?php
2
  /**
3
- * @package Wsal
4
  *
5
  * This Class load all the views, initialize them and shows the active one.
6
  * It creates also the menu items.
 
 
7
  */
8
- class WSAL_ViewManager
9
- {
10
- /**
11
- * @var WSAL_AbstractView[]
12
- */
13
- public $views = array();
14
-
15
- /**
16
- * @var WpSecurityAuditLog
17
- */
18
- protected $_plugin;
19
-
20
- /**
21
- * @var WSAL_AbstractView|null
22
- */
23
- protected $_active_view = false;
24
-
25
- public function __construct(WpSecurityAuditLog $plugin)
26
- {
27
- $this->_plugin = $plugin;
28
-
29
- // load views
30
- foreach (glob(dirname(__FILE__) . '/Views/*.php') as $file) {
31
- $this->AddFromFile($file);
32
- }
33
-
34
- // add menus
35
- add_action('admin_menu', array($this, 'AddAdminMenus'));
36
- add_action('network_admin_menu', array($this, 'AddAdminMenus'));
37
-
38
- // add plugin shortcut links
39
- add_filter('plugin_action_links_' . $plugin->GetBaseName(), array($this, 'AddPluginShortcuts'));
40
-
41
- // render header
42
- add_action('admin_enqueue_scripts', array($this, 'RenderViewHeader'));
43
-
44
- // render footer
45
- add_action('admin_footer', array($this, 'RenderViewFooter'));
46
- }
47
-
48
- /**
49
- * Add new view from file inside autoloader path.
50
- * @param string $file Path to file.
51
- */
52
- public function AddFromFile($file)
53
- {
54
- $this->AddFromClass($this->_plugin->GetClassFileClassName($file));
55
- }
56
-
57
- /**
58
- * Add new view given class name.
59
- * @param string $class Class name.
60
- */
61
- public function AddFromClass($class)
62
- {
63
- $this->AddInstance(new $class($this->_plugin));
64
- }
65
-
66
- /**
67
- * Add newly created view to list.
68
- * @param WSAL_AbstractView $view The new view.
69
- */
70
- public function AddInstance(WSAL_AbstractView $view)
71
- {
72
- $this->views[] = $view;
73
- }
74
-
75
- /**
76
- * Order views by their declared weight.
77
- */
78
- public function ReorderViews()
79
- {
80
- usort($this->views, array($this, 'OrderByWeight'));
81
- }
82
-
83
- /**
84
- * @internal This has to be public for PHP to call it.
85
- * @param WSAL_AbstractView $a
86
- * @param WSAL_AbstractView $b
87
- * @return int
88
- */
89
- public function OrderByWeight(WSAL_AbstractView $a, WSAL_AbstractView $b)
90
- {
91
- $wa = $a->GetWeight();
92
- $wb = $b->GetWeight();
93
- switch (true) {
94
- case $wa < $wb:
95
- return -1;
96
- case $wa > $wb:
97
- return 1;
98
- default:
99
- return 0;
100
- }
101
- }
102
-
103
- /**
104
- * Wordpress Action
105
- */
106
- public function AddAdminMenus()
107
- {
108
- $this->ReorderViews();
109
-
110
- if ($this->_plugin->settings->CurrentUserCan('view') && count($this->views)) {
111
- // add main menu
112
- $this->views[0]->hook_suffix = add_menu_page(
113
- 'WP Security Audit Log',
114
- 'Audit Log',
115
- 'read', // no capability requirement
116
- $this->views[0]->GetSafeViewName(),
117
- array($this, 'RenderViewBody'),
118
- $this->views[0]->GetIcon(),
119
- '2.5' // right after dashboard
120
- );
121
-
122
- // add menu items
123
- foreach ($this->views as $view) {
124
- if ($view->IsAccessible()) {
125
- if ($this->GetClassNameByView($view->GetName())) {
126
- continue;
127
- }
128
- $view->hook_suffix = add_submenu_page(
129
- $view->IsVisible() ? $this->views[0]->GetSafeViewName() : null,
130
- $view->GetTitle(),
131
- $view->GetName(),
132
- 'read', // no capability requirement
133
- $view->GetSafeViewName(),
134
- array($this, 'RenderViewBody'),
135
- $view->GetIcon()
136
- );
137
- }
138
- }
139
- }
140
- }
141
-
142
- /**
143
- * Wordpress Filter
144
- */
145
- public function AddPluginShortcuts($old_links)
146
- {
147
- $this->ReorderViews();
148
-
149
- $new_links = array();
150
- foreach ($this->views as $view) {
151
- if ($view->HasPluginShortcutLink()) {
152
- $new_links[] =
153
- '<a href="'
154
- . admin_url('admin.php?page=' . $view->GetSafeViewName())
155
- . '">'
156
- . $view->GetName()
157
- . '</a>';
158
- }
159
- }
160
- return array_merge($new_links, $old_links);
161
- }
162
-
163
- /**
164
- * @return int Returns page id of current page (or false on error).
165
- */
166
- protected function GetBackendPageIndex()
167
- {
168
- if (isset($_REQUEST['page'])) {
169
- foreach ($this->views as $i => $view) {
170
- if ($_REQUEST['page'] == $view->GetSafeViewName()) {
171
- return $i;
172
- }
173
- }
174
- }
175
- return false;
176
- }
177
-
178
- /**
179
- * @return WSAL_AbstractView|null Returns the current active view or null if none.
180
- */
181
- public function GetActiveView()
182
- {
183
- if ($this->_active_view === false) {
184
- $this->_active_view = null;
185
-
186
- if (isset($_REQUEST['page'])) {
187
- foreach ($this->views as $view) {
188
- if ($_REQUEST['page'] == $view->GetSafeViewName()) {
189
- $this->_active_view = $view;
190
- }
191
- }
192
- }
193
-
194
- if ($this->_active_view) {
195
- $this->_active_view->is_active = true;
196
- }
197
- }
198
- return $this->_active_view;
199
- }
200
-
201
- /**
202
- * Render header of the current view.
203
- */
204
- public function RenderViewHeader()
205
- {
206
- if (!!($view = $this->GetActiveView())) {
207
- $view->Header();
208
- }
209
- }
210
-
211
- /**
212
- * Render footer of the current view.
213
- */
214
- public function RenderViewFooter()
215
- {
216
- if (!!($view = $this->GetActiveView())) {
217
- $view->Footer();
218
- }
219
- }
220
-
221
- /**
222
- * Render content of the current view.
223
- */
224
- public function RenderViewBody()
225
- {
226
- $view = $this->GetActiveView();
227
- ?><div class="wrap"><?php
228
- $view->RenderIcon();
229
- $view->RenderTitle();
230
- $view->RenderContent();
231
- ?></div><?php
232
- }
233
-
234
- /**
235
- * Returns view instance corresponding to its class name.
236
- * @param string $className View class name.
237
- * @return WSAL_AbstractView The view or false on failure.
238
- */
239
- public function FindByClassName($className)
240
- {
241
- foreach ($this->views as $view) {
242
- if ($view instanceof $className) {
243
- return $view;
244
- }
245
- }
246
- return false;
247
- }
248
-
249
- private function GetClassNameByView($name_view)
250
- {
251
- $not_show = false;
252
- switch ($name_view) {
253
- case 'Notifications Email':
254
- if (class_exists('WSAL_NP_Plugin')) {
255
- $not_show = true;
256
- }
257
- break;
258
- case 'Logged In Users':
259
- if (class_exists('WSAL_User_Management_Plugin')) {
260
- $not_show = true;
261
- }
262
- break;
263
- case 'Reports':
264
- if (class_exists('WSAL_Rep_Plugin')) {
265
- $not_show = true;
266
- }
267
- break;
268
- case 'Search':
269
- if (class_exists('WSAL_SearchExtension')) {
270
- $not_show = true;
271
- }
272
- break;
273
- case 'External DB ':
274
- if (class_exists('WSAL_Ext_Plugin')) {
275
- $not_show = true;
276
- }
277
- break;
278
- case ' Add Functionality':
279
- if (class_exists('WSAL_NP_Plugin') ||
280
- class_exists('WSAL_User_Management_Plugin') ||
281
- class_exists('WSAL_Rep_Plugin') ||
282
- class_exists('WSAL_SearchExtension') ||
283
- class_exists('WSAL_Ext_Plugin')) {
284
- $not_show = true;
285
- }
286
- break;
287
- }
288
- return $not_show;
289
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
1
  <?php
2
  /**
3
+ * View Manager.
4
  *
5
  * This Class load all the views, initialize them and shows the active one.
6
  * It creates also the menu items.
7
+ *
8
+ * @package Wsal
9
  */
10
+ class WSAL_ViewManager {
11
+
12
+ /**
13
+ * Array of views.
14
+ *
15
+ * @var WSAL_AbstractView[]
16
+ */
17
+ public $views = array();
18
+
19
+ /**
20
+ * Instance of WpSecurityAuditLog.
21
+ *
22
+ * @var object
23
+ */
24
+ protected $_plugin;
25
+
26
+ /**
27
+ * Active view.
28
+ *
29
+ * @var WSAL_AbstractView|null
30
+ */
31
+ protected $_active_view = false;
32
+
33
+ /**
34
+ * Method: Constructor.
35
+ *
36
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
37
+ * @author Ashar Irfan
38
+ * @since 1.0.0
39
+ */
40
+ public function __construct( WpSecurityAuditLog $plugin ) {
41
+ $this->_plugin = $plugin;
42
+
43
+ $skip_views = array();
44
+
45
+ // Load views.
46
+ foreach ( glob( dirname( __FILE__ ) . '/Views/*.php' ) as $file ) {
47
+ if ( empty( $skip_views ) || ! in_array( $file, $skip_views ) ) {
48
+ $this->AddFromFile( $file );
49
+ }
50
+ }
51
+
52
+ // Add menus.
53
+ add_action( 'admin_menu', array( $this, 'AddAdminMenus' ) );
54
+ add_action( 'network_admin_menu', array( $this, 'AddAdminMenus' ) );
55
+
56
+ // Add plugin shortcut links.
57
+ add_filter( 'plugin_action_links_' . $plugin->GetBaseName(), array( $this, 'AddPluginShortcuts' ) );
58
+
59
+ // Render header.
60
+ add_action( 'admin_enqueue_scripts', array( $this, 'RenderViewHeader' ) );
61
+
62
+ // Render footer.
63
+ add_action( 'admin_footer', array( $this, 'RenderViewFooter' ) );
64
+ }
65
+
66
+ /**
67
+ * Add new view from file inside autoloader path.
68
+ *
69
+ * @param string $file Path to file.
70
+ */
71
+ public function AddFromFile( $file ) {
72
+ $this->AddFromClass( $this->_plugin->GetClassFileClassName( $file ) );
73
+ }
74
+
75
+ /**
76
+ * Add new view given class name.
77
+ *
78
+ * @param string $class Class name.
79
+ */
80
+ public function AddFromClass( $class ) {
81
+ $this->AddInstance( new $class( $this->_plugin ) );
82
+ }
83
+
84
+ /**
85
+ * Add newly created view to list.
86
+ *
87
+ * @param WSAL_AbstractView $view The new view.
88
+ */
89
+ public function AddInstance( WSAL_AbstractView $view ) {
90
+ $this->views[] = $view;
91
+ }
92
+
93
+ /**
94
+ * Order views by their declared weight.
95
+ */
96
+ public function ReorderViews() {
97
+ usort( $this->views, array( $this, 'OrderByWeight' ) );
98
+ }
99
+
100
+ /**
101
+ * Get page order by its weight.
102
+ *
103
+ * @internal This has to be public for PHP to call it.
104
+ * @param WSAL_AbstractView $a - First view.
105
+ * @param WSAL_AbstractView $b - Second view.
106
+ * @return int
107
+ */
108
+ public function OrderByWeight( WSAL_AbstractView $a, WSAL_AbstractView $b ) {
109
+ $wa = $a->GetWeight();
110
+ $wb = $b->GetWeight();
111
+ switch ( true ) {
112
+ case $wa < $wb:
113
+ return -1;
114
+ case $wa > $wb:
115
+ return 1;
116
+ default:
117
+ return 0;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * WordPress Action
123
+ */
124
+ public function AddAdminMenus() {
125
+ $this->ReorderViews();
126
+
127
+ if ( $this->_plugin->settings->CurrentUserCan( 'view' ) && count( $this->views ) ) {
128
+ // Add main menu.
129
+ $this->views[0]->hook_suffix = add_menu_page(
130
+ 'WP Security Audit Log',
131
+ 'Audit Log',
132
+ 'read', // No capability requirement.
133
+ $this->views[0]->GetSafeViewName(),
134
+ array( $this, 'RenderViewBody' ),
135
+ $this->views[0]->GetIcon(),
136
+ '2.5' // Right after dashboard.
137
+ );
138
+
139
+ // Add menu items.
140
+ foreach ( $this->views as $view ) {
141
+ if ( $view->IsAccessible() ) {
142
+ if ( $this->GetClassNameByView( $view->GetSafeViewName() ) ) {
143
+ continue;
144
+ }
145
+
146
+ $view->hook_suffix = add_submenu_page(
147
+ $view->IsVisible() ? $this->views[0]->GetSafeViewName() : null,
148
+ $view->GetTitle(),
149
+ $view->GetName(),
150
+ 'read', // No capability requirement.
151
+ $view->GetSafeViewName(),
152
+ array( $this, 'RenderViewBody' ),
153
+ $view->GetIcon()
154
+ );
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * WordPress Filter
162
+ *
163
+ * @param array $old_links - Array of old links.
164
+ */
165
+ public function AddPluginShortcuts( $old_links ) {
166
+ $this->ReorderViews();
167
+
168
+ $new_links = array();
169
+ foreach ( $this->views as $view ) {
170
+ if ( $view->HasPluginShortcutLink() ) {
171
+ $new_links[] =
172
+ '<a href="'
173
+ . admin_url( 'admin.php?page=' . $view->GetSafeViewName() )
174
+ . '">'
175
+ . $view->GetName()
176
+ . '</a>';
177
+ }
178
+ }
179
+ return array_merge( $new_links, $old_links );
180
+ }
181
+
182
+ /**
183
+ * Returns page id of current page (or false on error).
184
+ *
185
+ * @return int
186
+ */
187
+ protected function GetBackendPageIndex() {
188
+ // Get current view via $_GET array.
189
+ $current_view = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
190
+
191
+ if ( isset( $current_view ) ) {
192
+ foreach ( $this->views as $i => $view ) {
193
+ if ( $current_view === $view->GetSafeViewName() ) {
194
+ return $i;
195
+ }
196
+ }
197
+ }
198
+ return false;
199
+ }
200
+
201
+ /**
202
+ * Returns the current active view or null if none.
203
+ *
204
+ * @return WSAL_AbstractView|null
205
+ */
206
+ public function GetActiveView() {
207
+ if ( false === $this->_active_view ) {
208
+ $this->_active_view = null;
209
+
210
+ // Get current view via $_GET array.
211
+ $current_view = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING );
212
+
213
+ if ( isset( $current_view ) ) {
214
+ foreach ( $this->views as $view ) {
215
+ if ( $current_view === $view->GetSafeViewName() ) {
216
+ $this->_active_view = $view;
217
+ }
218
+ }
219
+ }
220
+
221
+ if ( $this->_active_view ) {
222
+ $this->_active_view->is_active = true;
223
+ }
224
+ }
225
+ return $this->_active_view;
226
+ }
227
+
228
+ /**
229
+ * Render header of the current view.
230
+ */
231
+ public function RenderViewHeader() {
232
+ if ( ! ! ($view = $this->GetActiveView()) ) {
233
+ $view->Header();
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Render footer of the current view.
239
+ */
240
+ public function RenderViewFooter() {
241
+ if ( ! ! ($view = $this->GetActiveView()) ) {
242
+ $view->Footer();
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Render content of the current view.
248
+ */
249
+ public function RenderViewBody() {
250
+ $view = $this->GetActiveView();
251
+ ?>
252
+ <div class="wrap">
253
+ <?php
254
+ $view->RenderIcon();
255
+ $view->RenderTitle();
256
+ $view->RenderContent();
257
+ ?>
258
+ </div>
259
+ <?php
260
+ }
261
+
262
+ /**
263
+ * Returns view instance corresponding to its class name.
264
+ *
265
+ * @param string $class_name View class name.
266
+ * @return WSAL_AbstractView The view or false on failure.
267
+ */
268
+ public function FindByClassName( $class_name ) {
269
+ foreach ( $this->views as $view ) {
270
+ if ( $view instanceof $class_name ) {
271
+ return $view;
272
+ }
273
+ }
274
+ return false;
275
+ }
276
+
277
+ /**
278
+ * Method: Returns class name of the view using view name.
279
+ *
280
+ * @param string $view_slug - Slug of view.
281
+ * @since 1.0.0
282
+ */
283
+ private function GetClassNameByView( $view_slug ) {
284
+ $not_show = false;
285
+ switch ( $view_slug ) {
286
+ case 'wsal-emailnotifications':
287
+ if ( class_exists( 'WSAL_NP_Plugin' ) ) {
288
+ $not_show = true;
289
+ }
290
+ break;
291
+ case 'wsal-loginusers':
292
+ if ( class_exists( 'WSAL_User_Management_Plugin' ) ) {
293
+ $not_show = true;
294
+ }
295
+ break;
296
+ case 'wsal-reports':
297
+ if ( class_exists( 'WSAL_Rep_Plugin' ) ) {
298
+ $not_show = true;
299
+ }
300
+ break;
301
+ case 'wsal-search':
302
+ if ( class_exists( 'WSAL_SearchExtension' ) ) {
303
+ $not_show = true;
304
+ }
305
+ break;
306
+ case 'wsal-externaldb':
307
+ if ( class_exists( 'WSAL_Ext_Plugin' ) ) {
308
+ $not_show = true;
309
+ }
310
+ break;
311
+ default:
312
+ break;
313
+ }
314
+ return $not_show;
315
+ }
316
  }
classes/Views/About.php DELETED
@@ -1,94 +0,0 @@
1
- <?php
2
- /**
3
- * @package Wsal
4
- *
5
- * About Page.
6
- */
7
- class WSAL_Views_About extends WSAL_AbstractView
8
- {
9
- public function GetTitle()
10
- {
11
- return __('About WP Security Audit Log', 'wp-security-audit-log');
12
- }
13
-
14
- public function GetIcon()
15
- {
16
- return 'dashicons-editor-help';
17
- }
18
-
19
- public function GetName()
20
- {
21
- return __('About', 'wp-security-audit-log');
22
- }
23
-
24
- public function GetWeight()
25
- {
26
- return 6;
27
- }
28
-
29
- public function Render()
30
- {
31
- ?><div class="metabox-holder" style="position: relative;">
32
-
33
- <div class="postbox" style="margin-right: 270px;">
34
- <!--h3 class="hndl"><span>About WP Security Audit Log</span></h3-->
35
- <div class="inside">
36
- <div class="activity-block">
37
- <?php _e('WP Security Audit Log enables WordPress administrators and owners to identify WordPress security issues before they become a security problem by keeping a security audit log. WP Security Audit Log is developed by WordPress security professionals WP White Security.', 'wp-security-audit-log'); ?>
38
-
39
- <h2><?php _e('Keep A WordPress Security Audit Log & Identify WordPress Security Issues', 'wp-security-audit-log'); ?></h2>
40
- <p>
41
- <?php _e('WP Security Audit Log logs everything happening on your WordPress blog or website and WordPress multisite network. By using WP Security Audit Log security plugin it is very easy to track suspicious user activity before it becomes a problem or a security issue. A WordPress security alert is generated by the plugin when:', 'wp-security-audit-log'); ?>
42
- </p>
43
- <ul style="list-style-type: disc; margin-left: 2.5em; list-style-position: outside;">
44
- <li><?php _e('User creates a new user or a new user is registered', 'wp-security-audit-log'); ?></li>
45
- <li><?php _e('Existing user changes the role, password or other properties of another user', 'wp-security-audit-log'); ?></li>
46
- <li><?php _e('Existing user on a WordPress multisite network is added to a site', 'wp-security-audit-log'); ?></li>
47
- <li><?php _e('User uploads or deletes a file, changes a password or email address', 'wp-security-audit-log'); ?></li>
48
- <li><?php _e('User installs, activates, deactivates, upgrades or uninstalls a plugin', 'wp-security-audit-log'); ?></li>
49
- <li><?php _e('User creates, modifies or deletes a new post, page, category or a custom post type', 'wp-security-audit-log'); ?></li>
50
- <li><?php _e('User installs or activates a WordPress theme', 'wp-security-audit-log'); ?></li>
51
- <li><?php _e('User adds, modifies or deletes a widget', 'wp-security-audit-log'); ?></li>
52
- <li><?php _e('User uses the dashboard file editor', 'wp-security-audit-log'); ?></li>
53
- <li><?php _e('WordPress settings are changed', 'wp-security-audit-log'); ?></li>
54
- <li><?php _e('Failed login attempts', 'wp-security-audit-log'); ?></li>
55
- <li><?php _e('and much more&hellip;', 'wp-security-audit-log'); ?></li>
56
- </ul>
57
- <br/>
58
- Refer to the complete list of <a href="http://www.wpsecurityauditlog.com/documentation/list-monitoring-wordpress-security-alerts-audit-log/?utm_source=wsalabt&utm_medium=txtlink&utm_campaign=wsal" target="_blank">WordPress Security Alerts</a> for more information.
59
- </div>
60
- </div>
61
- </div>
62
-
63
- <div style="position: absolute; right: 70px; width: 180px; top: 10px;">
64
- <div class="postbox">
65
- <h3 class="hndl"><span><?php _e('Extend the Functionality & Get More Value from WP Security Audit Log', 'wp-security-audit-log'); ?></span></h3>
66
- <div class="inside">
67
- <p>
68
- <?php _e('Get more value out of WP Security Audit Log by extending the functionality of WP Security Audit Log with the premium Add-Ons.'); ?>
69
- </p>
70
- <a class="button button-primary" href="http://www.wpsecurityauditlog.com/plugin-extensions/" target="_blank"><?php _e('See Add-Ons', 'wp-security-audit-log'); ?></a>
71
- </div>
72
- </div>
73
- <div class="postbox">
74
- <h3 class="hndl"><span><?php _e('WP Security Audit Log in your Language!', 'wp-security-audit-log'); ?></span></h3>
75
- <div class="inside">
76
- <?php _e('If you are interested in translating our plugin please drop us an email on', 'wp-security-audit-log'); ?>
77
- <a href="mailto:plugins@wpwhitesecurity.com">plugins@wpwhitesecurity.com</a>.
78
- </div>
79
- </div>
80
- <div class="postbox">
81
- <h3 class="hndl"><span><?php _e('WordPress Security Services', 'wp-security-audit-log'); ?></span></h3>
82
- <div class="inside">
83
- <?php _e('Professional WordPress security services provided by WP White Security', 'wp-security-audit-log'); ?>
84
- <ul>
85
- <li><a href="http://www.wpwhitesecurity.com/wordpress-security-services/wordpress-security-hardening/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">Security Hardening</a></li>
86
- <li><a href="http://www.wpwhitesecurity.com/wordpress-security-services/wordpress-security-audit/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">Security Audit</a></li>
87
- <li><a href="http://www.wpwhitesecurity.com/wordpress-security-services/wordpress-plugins-security-code-audit-review/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">Plugin Security Code Audit</a></li>
88
- </ul>
89
- </div>
90
- </div>
91
- </div>
92
- </div><?php
93
- }
94
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/Views/AuditLog.php CHANGED
@@ -4,7 +4,7 @@
4
  *
5
  * Class file for Audit Log View.
6
  *
7
- * @since 1.0.0
8
  * @package wsal
9
  */
10
 
@@ -39,9 +39,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
39
  /**
40
  * Method: Constructor
41
  *
42
- * @param object $plugin - Instance of WpSecurityAuditLog.
43
- * @author Ashar Irfan
44
- * @since 1.0.0
45
  */
46
  public function __construct( WpSecurityAuditLog $plugin ) {
47
  parent::__construct( $plugin );
@@ -65,52 +63,87 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
65
  public function AdminNoticesPremium() {
66
  $is_current_view = $this->_plugin->views->GetActiveView() == $this;
67
  // Check if any of the extensions is activated.
68
- if ( ! class_exists( 'WSAL_NP_Plugin' ) && ! class_exists( 'WSAL_SearchExtension' ) && ! class_exists( 'WSAL_Rep_Plugin' ) && ! class_exists( 'WSAL_Ext_Plugin' ) && ! class_exists( 'WSAL_User_Management_Plugin' ) ) {
 
 
 
 
69
  if ( current_user_can( 'manage_options' ) && $is_current_view && ! $this->IsNoticeDismissed( 'premium-wsal-' . $this->_version ) ) { ?>
70
- <div class="updated" data-notice-name="premium-wsal-<?php echo esc_attr( $this->_version ) ?>">
71
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=auditviewer&utm_medium=page&utm_campaign=plugin'; ?>
72
- <p><a href="<?php echo esc_attr( $url ); ?>" target="_blank"><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></a>
73
- <?php esc_html_e( 'and add Email Alerts, Reports, Search and Users Login and Session Management.', 'wp-security-audit-log' ); ?>
74
- <a href="<?php echo esc_attr( $url ); ?>" target="_blank"><?php esc_html_e( 'Upgrade Now!', 'wp-security-audit-log' ); ?></a>
75
- <a href="javascript:;" class="wsal-dismiss-notification wsal-premium"><span class="dashicons dashicons-dismiss"></span></a>
76
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  </div>
78
  <?php
79
  }
80
  }
81
  }
82
 
83
- public function HasPluginShortcutLink()
84
- {
 
 
85
  return true;
86
  }
87
 
88
- public function GetTitle()
89
- {
90
- return __('Audit Log Viewer', 'wp-security-audit-log');
 
 
91
  }
92
 
93
- public function GetIcon()
94
- {
 
 
95
  return $this->_wpversion < 3.8
96
  ? $this->_plugin->GetBaseUrl() . '/img/logo-main-menu.png'
97
- : 'dashicons-welcome-view-site';
98
  }
99
 
100
- public function GetName()
101
- {
102
- return __('Audit Log Viewer', 'wp-security-audit-log');
 
 
103
  }
104
 
105
- public function GetWeight()
106
- {
 
 
107
  return 1;
108
  }
109
 
110
- protected function GetListView()
111
- {
112
- if (is_null($this->_listview)) {
113
- $this->_listview = new WSAL_AuditLogListView($this->_plugin);
 
 
114
  }
115
  return $this->_listview;
116
  }
@@ -125,36 +158,50 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
125
  wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-security-audit-log' ) );
126
  }
127
 
 
 
 
 
 
 
 
 
128
  $this->GetListView()->prepare_items();
129
  $occ = new WSAL_Models_Occurrence();
130
 
131
- ?><form id="audit-log-viewer" method="post">
 
132
  <div id="audit-log-viewer-content">
133
- <input type="hidden" name="page" value="<?php echo esc_attr( $_REQUEST['page'] ); ?>" />
134
- <input type="hidden" id="wsal-cbid" name="wsal-cbid" value="<?php echo esc_attr( isset( $_REQUEST['wsal-cbid'] ) ? $_REQUEST['wsal-cbid'] : '0' ); ?>" />
135
  <?php do_action( 'wsal_auditlog_before_view', $this->GetListView() ); ?>
136
  <?php $this->GetListView()->display(); ?>
137
  <?php do_action( 'wsal_auditlog_after_view', $this->GetListView() ); ?>
138
  </div>
139
  </form>
140
 
141
- <?php if ( class_exists( 'WSAL_SearchExtension' ) &&
142
- ( isset( $_REQUEST['Filters'] ) || ( isset( $_REQUEST['s'] ) && trim( $_REQUEST['s'] ) ) ) ) : ?>
 
 
143
  <script type="text/javascript">
144
  jQuery(document).ready( function() {
145
  WsalAuditLogInit(
146
- <?php echo json_encode( array(
147
- 'ajaxurl' => admin_url( 'admin-ajax.php' ),
148
- 'tr8n' => array(
149
- 'numofitems' => __( 'Please enter the number of alerts you would like to see on one page:', 'wp-security-audit-log' ),
150
- 'searchback' => __( 'All Sites', 'wp-security-audit-log' ),
151
- 'searchnone' => __( 'No Results', 'wp-security-audit-log' ),
152
- ),
153
- 'autorefresh' => array(
154
- 'enabled' => false,
155
- 'token' => (int) $occ->Count(),
156
- ),
157
- ) );
 
 
 
158
  ?>
159
  );
160
  } );
@@ -163,67 +210,77 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
163
  <script type="text/javascript">
164
  jQuery(document).ready( function() {
165
  WsalAuditLogInit(
166
- <?php echo json_encode( array(
167
- 'ajaxurl' => admin_url( 'admin-ajax.php' ),
168
- 'tr8n' => array(
169
- 'numofitems' => __( 'Please enter the number of alerts you would like to see on one page:', 'wp-security-audit-log' ),
170
- 'searchback' => __( 'All Sites', 'wp-security-audit-log' ),
171
- 'searchnone' => __( 'No Results', 'wp-security-audit-log' ),
172
- ),
173
- 'autorefresh' => array(
174
- 'enabled' => $this->_plugin->settings->IsRefreshAlertsEnabled(),
175
- 'token' => (int) $occ->Count(),
176
- ),
177
- ) );
 
 
 
178
  ?>
179
  );
180
  } );
181
  </script>
182
- <?php endif;
 
183
  }
184
 
185
- public function AjaxInspector()
186
- {
187
- if (!$this->_plugin->settings->CurrentUserCan('view')) {
188
- die('Access Denied.');
189
  }
190
- if (!isset($_REQUEST['occurrence'])) {
191
- die('Occurrence parameter expected.');
 
 
 
 
192
  }
193
  $occ = new WSAL_Models_Occurrence();
194
- $occ->Load('id = %d', array((int)$_REQUEST['occurrence']));
195
 
196
  echo '<!DOCTYPE html><html><head>';
197
- echo '<link rel="stylesheet" id="open-sans-css" href="' . $this->_plugin->GetBaseUrl() . '/css/nice_r.css" type="text/css" media="all">';
198
- echo '<script type="text/javascript" src="'.$this->_plugin->GetBaseUrl() . '/js/nice_r.js"></script>';
199
  echo '<style type="text/css">';
200
  echo 'html, body { margin: 0; padding: 0; }';
201
  echo '.nice_r { position: absolute; padding: 8px; }';
202
  echo '.nice_r a { overflow: visible; }';
203
  echo '</style>';
204
  echo '</head><body>';
205
- $nicer = new WSAL_Nicer($occ->GetMetaArray());
206
  $nicer->render();
207
  echo '</body></html>';
208
  die;
209
  }
210
 
211
- public function AjaxRefresh()
212
- {
213
- if (!$this->_plugin->settings->CurrentUserCan('view')) {
214
- die('Access Denied.');
215
  }
216
- if (!isset($_REQUEST['logcount'])) {
217
- die('Log count parameter expected.');
 
 
 
 
218
  }
219
 
220
- $old = (int)$_REQUEST['logcount'];
221
  $max = 40; // 40*500msec = 20sec
222
 
223
  $is_archive = false;
224
- if ($this->_plugin->settings->IsArchivingEnabled()) {
225
- $selected_db = get_transient('wsal_wp_selected_db');
226
- if ($selected_db && $selected_db == 'archive') {
227
  $is_archive = true;
228
  }
229
  }
@@ -231,81 +288,87 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
231
  do {
232
  $occ = new WSAL_Models_Occurrence();
233
  $new = $occ->Count();
234
- usleep(500000); // 500msec
235
- } while (($old == $new) && (--$max > 0));
236
 
237
- if ($is_archive) {
238
  echo 'false';
239
  } else {
240
- echo $old == $new ? 'false' : $new;
241
  }
242
  die;
243
  }
244
 
245
- public function AjaxSetIpp()
246
- {
247
- if (!$this->_plugin->settings->CurrentUserCan('view')) {
248
- die('Access Denied.');
249
  }
250
- if (!isset($_REQUEST['count'])) {
251
- die('Count parameter expected.');
 
 
 
 
252
  }
253
- $this->_plugin->settings->SetViewPerPage((int)$_REQUEST['count']);
254
  die;
255
  }
256
 
257
- public function AjaxSearchSite()
258
- {
259
- if (!$this->_plugin->settings->CurrentUserCan('view')) {
260
- die('Access Denied.');
261
  }
262
- if (!isset($_REQUEST['search'])) {
263
- die('Search parameter expected.');
 
 
 
 
264
  }
265
  $grp1 = array();
266
  $grp2 = array();
267
 
268
- $search = $_REQUEST['search'];
269
 
270
- foreach ($this->GetListView()->get_sites() as $site) {
271
- if (stripos($site->blogname, $search) !== false) {
272
  $grp1[] = $site;
273
- } elseif (stripos($site->domain, $search) !== false) {
274
  $grp2[] = $site;
275
  }
276
  }
277
- die(json_encode(array_slice($grp1 + $grp2, 0, 7)));
278
  }
279
 
280
- public function AjaxSwitchDB()
281
- {
282
- if (isset($_REQUEST['selected_db'])) {
283
- set_transient('wsal_wp_selected_db', $_REQUEST['selected_db'], HOUR_IN_SECONDS);
 
 
284
  }
285
  }
286
 
287
- public function Header()
288
- {
289
  add_thickbox();
290
- wp_enqueue_style('darktooltip', $this->_plugin->GetBaseUrl() . '/css/darktooltip.css', array(), '');
291
  wp_enqueue_style(
292
  'auditlog',
293
  $this->_plugin->GetBaseUrl() . '/css/auditlog.css',
294
  array(),
295
- filemtime($this->_plugin->GetBaseDir() . '/css/auditlog.css')
296
  );
297
  }
298
 
299
- public function Footer()
300
- {
301
- wp_enqueue_script('jquery');
302
- wp_enqueue_script('darktooltip', $this->_plugin->GetBaseUrl() . '/js/jquery.darktooltip.js', array('jquery'), '');
303
- wp_enqueue_script('suggest');
304
  wp_enqueue_script(
305
  'auditlog',
306
  $this->_plugin->GetBaseUrl() . '/js/auditlog.js',
307
  array(),
308
- filemtime($this->_plugin->GetBaseDir() . '/js/auditlog.js')
309
  );
310
  }
311
  }
4
  *
5
  * Class file for Audit Log View.
6
  *
7
+ * @since 1.0.0
8
  * @package wsal
9
  */
10
 
39
  /**
40
  * Method: Constructor
41
  *
42
+ * @param WpSecurityAuditLog $plugin - Instance of WpSecurityAuditLog.
 
 
43
  */
44
  public function __construct( WpSecurityAuditLog $plugin ) {
45
  parent::__construct( $plugin );
63
  public function AdminNoticesPremium() {
64
  $is_current_view = $this->_plugin->views->GetActiveView() == $this;
65
  // Check if any of the extensions is activated.
66
+ if ( ! class_exists( 'WSAL_NP_Plugin' )
67
+ && ! class_exists( 'WSAL_Ext_Plugin' )
68
+ && ! class_exists( 'WSAL_Rep_Plugin' )
69
+ && ! class_exists( 'WSAL_SearchExtension' )
70
+ && ! class_exists( 'WSAL_User_Management_Plugin' ) ) {
71
  if ( current_user_can( 'manage_options' ) && $is_current_view && ! $this->IsNoticeDismissed( 'premium-wsal-' . $this->_version ) ) { ?>
72
+ <div class="updated wsal_notice" data-notice-name="premium-wsal-<?php echo esc_attr( $this->_version ); ?>">
73
+ <div class="wsal_notice__wrapper">
74
+ <img src="<?php echo esc_url( WSAL_BASE_URL ); ?>img/wsal-logo@2x.png">
75
+ <p>
76
+ <strong><?php esc_html_e( 'See who is logged in to your WordPress, create user productivity reports, get alerted via email of important changes and more!', 'wp-security-audit-log' ); ?></strong><br />
77
+ <?php esc_html_e( 'Unlock these powerful features and much more with the premium edition of WP Security Audit Log.', 'wp-security-audit-log' ); ?>
78
+ </p>
79
+ <!-- /.wsal_notice__wrapper -->
80
+ <div class="wsal_notice__btns">
81
+ <?php
82
+ $buy_now = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
83
+ $more_info = add_query_arg(
84
+ array(
85
+ 'utm_source' => 'plugin',
86
+ 'utm_medium' => 'banner',
87
+ 'utm_content' => 'audit+log+viewier+more+info',
88
+ 'utm_campaign' => 'upgrade+premium',
89
+ ),
90
+ 'https://www.wpsecurityauditlog.com/premium-features/'
91
+ );
92
+ ?>
93
+ <a href="<?php echo esc_url( $buy_now ); ?>" class="button button-primary buy-now"><?php esc_html_e( 'Buy Now', 'wp-security-audit-log' ); ?></a>
94
+ <a href="<?php echo esc_url( $more_info ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'wp-security-audit-log' ); ?></a>
95
+ </div>
96
+ <!-- /.wsal_notice__btns -->
97
+ </div>
98
  </div>
99
  <?php
100
  }
101
  }
102
  }
103
 
104
+ /**
105
+ * Method: Check if view has shortcut link.
106
+ */
107
+ public function HasPluginShortcutLink() {
108
  return true;
109
  }
110
 
111
+ /**
112
+ * Method: Get View Title.
113
+ */
114
+ public function GetTitle() {
115
+ return __( 'Audit Log Viewer', 'wp-security-audit-log' );
116
  }
117
 
118
+ /**
119
+ * Method: Get View Icon.
120
+ */
121
+ public function GetIcon() {
122
  return $this->_wpversion < 3.8
123
  ? $this->_plugin->GetBaseUrl() . '/img/logo-main-menu.png'
124
+ : $this->_plugin->GetBaseUrl() . '/img/wsal-menu-icon.svg';
125
  }
126
 
127
+ /**
128
+ * Method: Get View Name.
129
+ */
130
+ public function GetName() {
131
+ return __( 'Audit Log Viewer', 'wp-security-audit-log' );
132
  }
133
 
134
+ /**
135
+ * Method: Get View Weight.
136
+ */
137
+ public function GetWeight() {
138
  return 1;
139
  }
140
 
141
+ /**
142
+ * Method: Get View.
143
+ */
144
+ protected function GetListView() {
145
+ if ( is_null( $this->_listview ) ) {
146
+ $this->_listview = new WSAL_AuditLogListView( $this->_plugin );
147
  }
148
  return $this->_listview;
149
  }
158
  wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-security-audit-log' ) );
159
  }
160
 
161
+ // Filter $_POST array for security.
162
+ $post_array = filter_input_array( INPUT_POST );
163
+
164
+ // Verify nonce for security.
165
+ if ( isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'bulk-logs' ) ) {
166
+ wp_die( esc_html__( 'Nonce verification failed.', 'wp-security-audit-log' ) );
167
+ }
168
+
169
  $this->GetListView()->prepare_items();
170
  $occ = new WSAL_Models_Occurrence();
171
 
172
+ ?>
173
+ <form id="audit-log-viewer" method="post">
174
  <div id="audit-log-viewer-content">
175
+ <input type="hidden" name="page" value="<?php echo filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING ); ?>" />
176
+ <input type="hidden" id="wsal-cbid" name="wsal-cbid" value="<?php echo esc_attr( isset( $post_array['wsal-cbid'] ) ? $post_array['wsal-cbid'] : '0' ); ?>" />
177
  <?php do_action( 'wsal_auditlog_before_view', $this->GetListView() ); ?>
178
  <?php $this->GetListView()->display(); ?>
179
  <?php do_action( 'wsal_auditlog_after_view', $this->GetListView() ); ?>
180
  </div>
181
  </form>
182
 
183
+ <?php
184
+ if ( class_exists( 'WSAL_SearchExtension' ) &&
185
+ ( isset( $post_array['Filters'] ) || ( isset( $post_array['s'] ) && trim( $post_array['s'] ) ) ) ) :
186
+ ?>
187
  <script type="text/javascript">
188
  jQuery(document).ready( function() {
189
  WsalAuditLogInit(
190
+ <?php
191
+ echo json_encode(
192
+ array(
193
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
194
+ 'tr8n' => array(
195
+ 'numofitems' => __( 'Please enter the number of alerts you would like to see on one page:', 'wp-security-audit-log' ),
196
+ 'searchback' => __( 'All Sites', 'wp-security-audit-log' ),
197
+ 'searchnone' => __( 'No Results', 'wp-security-audit-log' ),
198
+ ),
199
+ 'autorefresh' => array(
200
+ 'enabled' => false,
201
+ 'token' => (int) $occ->Count(),
202
+ ),
203
+ )
204
+ );
205
  ?>
206
  );
207
  } );
210
  <script type="text/javascript">
211
  jQuery(document).ready( function() {
212
  WsalAuditLogInit(
213
+ <?php
214
+ echo json_encode(
215
+ array(
216
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
217
+ 'tr8n' => array(
218
+ 'numofitems' => __( 'Please enter the number of alerts you would like to see on one page:', 'wp-security-audit-log' ),
219
+ 'searchback' => __( 'All Sites', 'wp-security-audit-log' ),
220
+ 'searchnone' => __( 'No Results', 'wp-security-audit-log' ),
221
+ ),
222
+ 'autorefresh' => array(
223
+ 'enabled' => $this->_plugin->settings->IsRefreshAlertsEnabled(),
224
+ 'token' => (int) $occ->Count(),
225
+ ),
226
+ )
227
+ );
228
  ?>
229
  );
230
  } );
231
  </script>
232
+ <?php
233
+ endif;
234
  }
235
 
236
+ public function AjaxInspector() {
237
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
238
+ die( 'Access Denied.' );
 
239
  }
240
+
241
+ // Filter $_GET array for security.
242
+ $get_array = filter_input_array( INPUT_GET );
243
+
244
+ if ( ! isset( $get_array['occurrence'] ) ) {
245
+ die( 'Occurrence parameter expected.' );
246
  }
247
  $occ = new WSAL_Models_Occurrence();
248
+ $occ->Load( 'id = %d', array( (int) $get_array['occurrence'] ) );
249
 
250
  echo '<!DOCTYPE html><html><head>';
251
+ echo '<link rel="stylesheet" id="open-sans-css" href="' . esc_url( $this->_plugin->GetBaseUrl() ) . '/css/nice_r.css" type="text/css" media="all">';
252
+ echo '<script type="text/javascript" src="' . esc_url( $this->_plugin->GetBaseUrl() ) . '/js/nice_r.js"></script>';
253
  echo '<style type="text/css">';
254
  echo 'html, body { margin: 0; padding: 0; }';
255
  echo '.nice_r { position: absolute; padding: 8px; }';
256
  echo '.nice_r a { overflow: visible; }';
257
  echo '</style>';
258
  echo '</head><body>';
259
+ $nicer = new WSAL_Nicer( $occ->GetMetaArray() );
260
  $nicer->render();
261
  echo '</body></html>';
262
  die;
263
  }
264
 
265
+ public function AjaxRefresh() {
266
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
267
+ die( 'Access Denied.' );
 
268
  }
269
+
270
+ // Filter $_POST array for security.
271
+ $post_array = filter_input_array( INPUT_POST );
272
+
273
+ if ( ! isset( $post_array['logcount'] ) ) {
274
+ die( 'Log count parameter expected.' );
275
  }
276
 
277
+ $old = (int) $post_array['logcount'];
278
  $max = 40; // 40*500msec = 20sec
279
 
280
  $is_archive = false;
281
+ if ( $this->_plugin->settings->IsArchivingEnabled() ) {
282
+ $selected_db = get_transient( 'wsal_wp_selected_db' );
283
+ if ( $selected_db && 'archive' == $selected_db ) {
284
  $is_archive = true;
285
  }
286
  }
288
  do {
289
  $occ = new WSAL_Models_Occurrence();
290
  $new = $occ->Count();
291
+ usleep( 500000 ); // 500msec
292
+ } while ( ($old == $new) && (--$max > 0) );
293
 
294
+ if ( $is_archive ) {
295
  echo 'false';
296
  } else {
297
+ echo $old == $new ? 'false' : esc_html( $new );
298
  }
299
  die;
300
  }
301
 
302
+ public function AjaxSetIpp() {
303
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
304
+ die( 'Access Denied.' );
 
305
  }
306
+
307
+ // Filter $_POST array for security.
308
+ $post_array = filter_input_array( INPUT_POST );
309
+
310
+ if ( ! isset( $post_array['count'] ) ) {
311
+ die( 'Count parameter expected.' );
312
  }
313
+ $this->_plugin->settings->SetViewPerPage( (int) $post_array['count'] );
314
  die;
315
  }
316
 
317
+ public function AjaxSearchSite() {
318
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
319
+ die( 'Access Denied.' );
 
320
  }
321
+
322
+ // Filter $_POST array for security.
323
+ $post_array = filter_input_array( INPUT_POST );
324
+
325
+ if ( ! isset( $post_array['search'] ) ) {
326
+ die( 'Search parameter expected.' );
327
  }
328
  $grp1 = array();
329
  $grp2 = array();
330
 
331
+ $search = $post_array['search'];
332
 
333
+ foreach ( $this->GetListView()->get_sites() as $site ) {
334
+ if ( stripos( $site->blogname, $search ) !== false ) {
335
  $grp1[] = $site;
336
+ } elseif ( stripos( $site->domain, $search ) !== false ) {
337
  $grp2[] = $site;
338
  }
339
  }
340
+ die( json_encode( array_slice( $grp1 + $grp2, 0, 7 ) ) );
341
  }
342
 
343
+ public function AjaxSwitchDB() {
344
+ // Filter $_POST array for security.
345
+ $post_array = filter_input_array( INPUT_POST );
346
+
347
+ if ( isset( $post_array['selected_db'] ) ) {
348
+ set_transient( 'wsal_wp_selected_db', $post_array['selected_db'], HOUR_IN_SECONDS );
349
  }
350
  }
351
 
352
+ public function Header() {
 
353
  add_thickbox();
354
+ wp_enqueue_style( 'darktooltip', $this->_plugin->GetBaseUrl() . '/css/darktooltip.css', array(), '' );
355
  wp_enqueue_style(
356
  'auditlog',
357
  $this->_plugin->GetBaseUrl() . '/css/auditlog.css',
358
  array(),
359
+ filemtime( $this->_plugin->GetBaseDir() . '/css/auditlog.css' )
360
  );
361
  }
362
 
363
+ public function Footer() {
364
+ wp_enqueue_script( 'jquery' );
365
+ wp_enqueue_script( 'darktooltip', $this->_plugin->GetBaseUrl() . '/js/jquery.darktooltip.js', array( 'jquery' ), '' );
366
+ wp_enqueue_script( 'suggest' );
 
367
  wp_enqueue_script(
368
  'auditlog',
369
  $this->_plugin->GetBaseUrl() . '/js/auditlog.js',
370
  array(),
371
+ filemtime( $this->_plugin->GetBaseDir() . '/js/auditlog.js' )
372
  );
373
  }
374
  }
classes/Views/EmailNotifications.php CHANGED
@@ -1,65 +1,175 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
 
5
  * Email Notifications Add-On promo Page.
6
  * Used only if the plugin is not activated.
 
 
7
  */
8
- class WSAL_Views_EmailNotifications extends WSAL_AbstractView
9
- {
10
-
11
- public function GetTitle()
12
- {
13
- return __('Email Notifications Add-On', 'wp-security-audit-log');
14
- }
15
-
16
- public function GetIcon()
17
- {
18
- return 'dashicons-external';
19
- }
20
-
21
- public function GetName()
22
- {
23
- return __('Notifications Email', 'wp-security-audit-log');
24
- }
25
-
26
- public function GetWeight()
27
- {
28
- return 7;
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- public function Header()
32
- {
33
- wp_enqueue_style(
34
- 'extensions',
35
- $this->_plugin->GetBaseUrl() . '/css/extensions.css',
36
- array(),
37
- filemtime($this->_plugin->GetBaseDir() . '/css/extensions.css')
38
- );
39
- }
40
-
41
- public function Render()
42
- {
43
- ?>
44
- <div class="wrap-advertising-page-single">
45
- <div class="icon" style='background-image:url("<?=$this->_plugin->GetBaseUrl();?>/img/envelope.jpg");'></div>
46
- <h3><?php _e('Email Notifications', 'wp-security-audit-log'); ?></h3>
47
- <p>
48
- <?php _e('This premium add-on allows you to configure email alerts so you are <br>notified instantly when important changes happen on your WordPress.', 'wp-security-audit-log'); ?>
49
- </p>
50
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/wordpress-email-notifications-add-on/?utm_source=plugin&utm_medium=emailpage&utm_campaign=notifications'; ?>
51
- <p>
52
- <a class="button-primary" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Learn More', 'wp-security-audit-log'); ?></a>
53
- </p>
54
- <div class="clear"></div>
55
- <p>
56
- <span class="description">
57
- <strong><span class="text-red">70% Off</span> when you purchase this add-on as part of the All Add-On bundle.</strong>
58
- </span>
59
- </p>
60
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=alladdons'; ?>
61
- <a class="button-blue" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Buy all Add-Ons Bundle', 'wp-security-audit-log'); ?></a>
62
- </div>
63
- <?php
64
- }
65
  }
1
  <?php
2
  /**
3
+ * View: Email Notifications Page
4
+ *
5
+ * WSAL email notifications page.
6
  *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Email Notifications Add-On promo Page.
18
  * Used only if the plugin is not activated.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Views_EmailNotifications extends WSAL_AbstractView {
23
+
24
+ /**
25
+ * Method: Get View Title.
26
+ */
27
+ public function GetTitle() {
28
+ return __( 'Email Notifications Add-On', 'wp-security-audit-log' );
29
+ }
30
+
31
+ /**
32
+ * Method: Get View Icon.
33
+ */
34
+ public function GetIcon() {
35
+ return 'dashicons-external';
36
+ }
37
+
38
+ /**
39
+ * Method: Get View Name.
40
+ */
41
+ public function GetName() {
42
+ return __( 'Email Notifications &#8682;', 'wp-security-audit-log' );
43
+ }
44
+
45
+ /**
46
+ * Method: Get View Weight.
47
+ */
48
+ public function GetWeight() {
49
+ return 9;
50
+ }
51
+
52
+ /**
53
+ * Method: Get View Header.
54
+ */
55
+ public function Header() {
56
+ // Extension Page CSS.
57
+ wp_enqueue_style(
58
+ 'extensions',
59
+ $this->_plugin->GetBaseUrl() . '/css/extensions.css',
60
+ array(),
61
+ filemtime( $this->_plugin->GetBaseDir() . '/css/extensions.css' )
62
+ );
63
+
64
+ // Swipebox CSS.
65
+ wp_enqueue_style(
66
+ 'wsal-swipebox-css',
67
+ $this->_plugin->GetBaseUrl() . '/css/swipebox.min.css',
68
+ array(),
69
+ filemtime( $this->_plugin->GetBaseDir() . '/css/swipebox.min.css' )
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Method: Get View Footer.
75
+ */
76
+ public function Footer() {
77
+ // jQuery.
78
+ wp_enqueue_script( 'jquery' );
79
+
80
+ // Swipebox JS.
81
+ wp_register_script(
82
+ 'wsal-swipebox-js',
83
+ $this->_plugin->GetBaseUrl() . '/js/jquery.swipebox.min.js',
84
+ array( 'jquery' ),
85
+ filemtime( $this->_plugin->GetBaseDir() . '/js/jquery.swipebox.min.js' )
86
+ );
87
+ wp_enqueue_script( 'wsal-swipebox-js' );
88
+
89
+ // Extensions JS.
90
+ wp_register_script(
91
+ 'wsal-extensions-js',
92
+ $this->_plugin->GetBaseUrl() . '/js/extensions.js',
93
+ array( 'wsal-swipebox-js' ),
94
+ filemtime( $this->_plugin->GetBaseDir() . '/js/extensions.js' )
95
+ );
96
+ wp_enqueue_script( 'wsal-extensions-js' );
97
+ }
98
+
99
+ /**
100
+ * Method: Get View Render.
101
+ */
102
+ public function Render() {
103
+ ?>
104
+ <div class="wrap-advertising-page-single">
105
+ <div class="wsal-row">
106
+ <div class="wsal-col">
107
+ <div class="icon" style='background-image:url("<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/envelope.jpg");'></div>
108
+ </div>
109
+ <!-- /.wsal-col -->
110
+
111
+ <div class="wsal-col">
112
+ <h3><?php esc_html_e( 'Email Notifications', 'wp-security-audit-log' ); ?></h3>
113
+ <p>
114
+ <?php esc_html_e( 'Upgrade to Premium to:', 'wp-security-audit-log' ); ?>
115
+ </p>
116
+ <p>
117
+ <ul class="wsal-features-list">
118
+ <li><?php esc_html_e( 'Configure email notifications to be instantly alerted of important changes,', 'wp-security-audit-log' ); ?></li>
119
+ <li><?php esc_html_e( 'Configure notifications for when users login, change content, install a plugin or do any other change,', 'wp-security-audit-log' ); ?></li>
120
+ <li><?php esc_html_e( 'Configure security email notifications,', 'wp-security-audit-log' ); ?></li>
121
+ <li><?php esc_html_e( 'Configure email notifications via a user friendly wizard,', 'wp-security-audit-log' ); ?></li>
122
+ <li><?php esc_html_e( 'Edit and create your own templates for email notifications,', 'wp-security-audit-log' ); ?></li>
123
+ <li><?php esc_html_e( '& more.', 'wp-security-audit-log' ); ?></li>
124
+ </ul>
125
+ </p>
126
+ <?php
127
+ $buy_now = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
128
+ $more_info = add_query_arg(
129
+ array(
130
+ 'utm_source' => 'plugin',
131
+ 'utm_medium' => 'page',
132
+ 'utm_content' => 'email+more+info',
133
+ 'utm_campaign' => 'upgrade+premium',
134
+ ),
135
+ 'https://www.wpsecurityauditlog.com/premium-features/'
136
+ );
137
+ ?>
138
+ <p>
139
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $buy_now ); ?>"><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></a>
140
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $more_info ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'wp-security-audit-log' ); ?></a>
141
+ </p>
142
+ </div>
143
+ <!-- /.wsal-col -->
144
+ </div>
145
+ <!-- /.wsal-row -->
146
+
147
+ <div class="wsal-row">
148
+ <div class="wsal-col">
149
+ <h3><?php esc_html_e( 'Screenshots', 'wp-security-audit-log' ); ?></h3>
150
 
151
+ <p>
152
+ <ul class="wsal-features-list">
153
+ <li>
154
+ <?php esc_html_e( 'Use the trigger builder to configure any type of email notification so you are instantly alerted of important changes on your WordPress.', 'wp-security-audit-log' ); ?><br />
155
+ <a class="swipebox" title="<?php esc_attr_e( 'Use the trigger builder to configure any type of email notification so you are instantly alerted of important changes on your WordPress.', 'wp-security-audit-log' ); ?>"
156
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/email-notifications/email_notifications.png">
157
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/email-notifications/email_notifications.png">
158
+ </a>
159
+ </li>
160
+ <li>
161
+ <?php esc_html_e( 'Use the wizard to easily get started and quickly configure basic email notifications.', 'wp-security-audit-log' ); ?><br />
162
+ <a class="swipebox" title="<?php esc_attr_e( 'Use the wizard to easily get started and quickly configure basic email notifications.', 'wp-security-audit-log' ); ?>"
163
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/email-notifications/email_notifications_wizard.png">
164
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/email-notifications/email_notifications_wizard.png">
165
+ </a>
166
+ </li>
167
+ </ul>
168
+ </p>
169
+ </div>
170
+ </div>
171
+ <!-- /.wsal-row -->
172
+ </div>
173
+ <?php
174
+ }
 
 
 
 
 
 
 
 
 
 
175
  }
classes/Views/Extensions.php DELETED
@@ -1,137 +0,0 @@
1
- <?php
2
- /**
3
- * @package Wsal
4
- *
5
- * All Add-Ons promo Page.
6
- * Used only if no plugins are activated.
7
- */
8
- class WSAL_Views_Extensions extends WSAL_AbstractView
9
- {
10
-
11
- public function GetTitle()
12
- {
13
- return __('WP Security Audit Log Add-Ons', 'wp-security-audit-log');
14
- }
15
-
16
- public function GetIcon()
17
- {
18
- return 'dashicons-external';
19
- }
20
-
21
- public function GetName()
22
- {
23
- return __(' Add Functionality', 'wp-security-audit-log');
24
- }
25
-
26
- public function GetWeight()
27
- {
28
- return 3.5;
29
- }
30
-
31
- public function Header()
32
- {
33
- wp_enqueue_style(
34
- 'extensions',
35
- $this->_plugin->GetBaseUrl() . '/css/extensions.css',
36
- array(),
37
- filemtime($this->_plugin->GetBaseDir() . '/css/extensions.css')
38
- );
39
- }
40
-
41
- public function Render()
42
- {
43
- ?>
44
- <p><?php _e('The below add-ons allow you to extend the functionality of WP Security Audit Log plugin and enable you to get more benefits out of the WordPress security audit, such as configurable email alerts, ability to search using free text based searches & filters, and generate user activity reports to meet regulatory compliance requirements.', 'wp-security-audit-log'); ?>
45
- </p>
46
- <div class="wrap-advertising-page">
47
- <div class="extension all">
48
- <?php $link = 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=alladdons'; ?>
49
- <a target="_blank" href="<?php echo esc_attr($link); ?>">
50
- <h3><?php _e('All Add-Ons Bundle', 'wp-security-audit-log'); ?></h3>
51
- </a>
52
- <p><?php _e('Benefit from a 60% discount when you purchase all the add-ons as a single bundle.', 'wp-security-audit-log'); ?>
53
- </p>
54
- <p>
55
- <a target="_blank" href="<?php echo esc_attr($link); ?>" class="button-primary"><?php _e('Get this Bundle', 'wp-security-audit-log'); ?>
56
- </a>
57
- </p>
58
- </div>
59
- <?php if (!class_exists('WSAL_User_Management_Plugin')) { ?>
60
- <div class="extension user-managment">
61
- <?php $link = 'https://www.wpsecurityauditlog.com/extensions/user-sessions-management-wp-security-audit-log/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=logins'; ?>
62
- <a target="_blank" href="<?php echo esc_attr($link); ?>">
63
- <h3><?php _e('Users Logins and Management', 'wp-security-audit-log'); ?></h3>
64
- </a>
65
- <p><?php _e('See who is logged in to your WordPress and manage user sessions and logins.', 'wp-security-audit-log'); ?>
66
-
67
- </p>
68
- <p>
69
- <a target="_blank" href="<?php echo esc_attr($link); ?>" class="button-primary"><?php _e('Get this extension', 'wp-security-audit-log'); ?>
70
- </a>
71
- </p>
72
- </div>
73
- <?php } ?>
74
- <?php if (!class_exists('WSAL_NP_Plugin')) { ?>
75
- <div class="extension email-notifications">
76
- <?php $link = 'https://www.wpsecurityauditlog.com/extensions/wordpress-email-notifications-add-on/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=notifications'; ?>
77
- <a target="_blank" href="<?php echo esc_attr($link); ?>">
78
- <h3><?php _e('Email Notifications', 'wp-security-audit-log'); ?></h3>
79
- </a>
80
- <p><?php _e('Get notified instantly via email when important changes are made on your WordPress!', 'wp-security-audit-log'); ?>
81
-
82
- </p>
83
- <p>
84
- <a target="_blank" href="<?php echo esc_attr($link); ?>" class="button-primary"><?php _e('Get this extension', 'wp-security-audit-log'); ?>
85
- </a>
86
- </p>
87
- </div>
88
- <?php } ?>
89
- <?php if (!class_exists('WSAL_Rep_Plugin')) { ?>
90
- <div class="extension reports">
91
- <?php $link = 'https://www.wpsecurityauditlog.com/extensions/compliance-reports-add-on-for-wordpress/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=reports'; ?>
92
- <a target="_blank" href="<?php echo esc_attr($link); ?>">
93
- <h3><?php _e('Reports', 'wp-security-audit-log'); ?></h3>
94
- </a>
95
- <p><?php _e('Generate any type of user,site and activity report to keep track of user productivity and to meet regulatory compliance requirements.', 'wp-security-audit-log'); ?>
96
-
97
- </p>
98
- <p>
99
- <a target="_blank" href="<?php echo esc_attr($link); ?>" class="button-primary"><?php _e('Get this extension', 'wp-security-audit-log'); ?>
100
- </a>
101
- </p>
102
- </div>
103
- <?php } ?>
104
- <?php if (!class_exists('WSAL_SearchExtension')) { ?>
105
- <div class="extension search-ext">
106
- <?php $link = 'https://www.wpsecurityauditlog.com/extensions/search-add-on-for-wordpress-security-audit-log/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=search'; ?>
107
- <a target="_blank" href="<?php echo esc_attr($link); ?>">
108
- <h3><?php _e('Search', 'wp-security-audit-log'); ?></h3>
109
- </a>
110
- <p><?php _e('Do free-text based searches for specific activity in the WordPress audit trail. You can also use filters to fine-tune your searches.', 'wp-security-audit-log'); ?>
111
-
112
- </p>
113
- <p>
114
- <a target="_blank" href="<?php echo esc_attr($link); ?>" class="button-primary"><?php _e('Get this extension', 'wp-security-audit-log'); ?>
115
- </a>
116
- </p>
117
- </div>
118
- <?php } ?>
119
- <?php if (!class_exists('WSAL_Ext_Plugin')) { ?>
120
- <div class="extension external-db">
121
- <?php $link = 'https://www.wpsecurityauditlog.com/extensions/external-database-for-wp-security-audit-log/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=externaldb'; ?>
122
- <a target="_blank" href="<?php echo esc_attr($link); ?>">
123
- <h3><?php _e('External DB', 'wp-security-audit-log'); ?></h3>
124
- </a>
125
- <p><?php _e('Store the WordPress audit trial in an external database for a more secure and faster WordPress website.', 'wp-security-audit-log'); ?>
126
-
127
- </p>
128
- <p>
129
- <a target="_blank" href="<?php echo esc_attr($link); ?>" class="button-primary"><?php _e('Get this extension', 'wp-security-audit-log'); ?>
130
- </a>
131
- </p>
132
- </div>
133
- <?php } ?>
134
- </div>
135
- <?php
136
- }
137
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/Views/ExternalDB.php CHANGED
@@ -1,65 +1,174 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
 
5
  * External DB Add-On promo Page.
6
  * Used only if the plugin is not activated.
 
 
7
  */
8
- class WSAL_Views_ExternalDB extends WSAL_AbstractView
9
- {
10
-
11
- public function GetTitle()
12
- {
13
- return __('External DB Add-On', 'wp-security-audit-log');
14
- }
15
-
16
- public function GetIcon()
17
- {
18
- return 'dashicons-external';
19
- }
20
-
21
- public function GetName()
22
- {
23
- return __('External DB ', 'wp-security-audit-log');
24
- }
25
-
26
- public function GetWeight()
27
- {
28
- return 10;
29
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
- public function Header()
32
- {
33
- wp_enqueue_style(
34
- 'extensions',
35
- $this->_plugin->GetBaseUrl() . '/css/extensions.css',
36
- array(),
37
- filemtime($this->_plugin->GetBaseDir() . '/css/extensions.css')
38
- );
39
- }
40
-
41
- public function Render()
42
- {
43
- ?>
44
- <div class="wrap-advertising-page-single">
45
- <div class="icon" style='background-image:url("<?=$this->_plugin->GetBaseUrl();?>/img/database.jpg");'></div>
46
- <h3><?php _e('External DB', 'wp-security-audit-log'); ?></h3>
47
- <p>
48
- <?php _e('Meet Legal and Regulatory Compliance Requirements, Improve the Security of Your WordPress and Boost its Performance by storing the WordPress Audit Trail in an external database.', 'wp-security-audit-log'); ?>
49
- </p>
50
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/external-database-for-wp-security-audit-log/?utm_source=plugin&utm_medium=externaldbpage&utm_campaign=externaldb'; ?>
51
- <p>
52
- <a class="button-primary" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Learn More', 'wp-security-audit-log'); ?></a>
53
- </p>
54
- <div class="clear"></div>
55
- <p>
56
- <span class="description">
57
- <strong><span class="text-red">70% Off</span> when you purchase this add-on as part of the All Add-On bundle.</strong>
58
- </span>
59
- </p>
60
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=alladdons'; ?>
61
- <a class="button-blue" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Buy all Add-Ons Bundle', 'wp-security-audit-log'); ?></a>
62
- </div>
63
- <?php
64
- }
65
  }
1
  <?php
2
  /**
3
+ * View: External DB Page
4
+ *
5
+ * WSAL external db page.
6
  *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * External DB Add-On promo Page.
18
  * Used only if the plugin is not activated.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Views_ExternalDB extends WSAL_AbstractView {
23
+
24
+ /**
25
+ * Method: Get View Title.
26
+ */
27
+ public function GetTitle() {
28
+ return __( 'External DB Add-On', 'wp-security-audit-log' );
29
+ }
30
+
31
+ /**
32
+ * Method: Get View Icon.
33
+ */
34
+ public function GetIcon() {
35
+ return 'dashicons-external';
36
+ }
37
+
38
+ /**
39
+ * Method: Get View Name.
40
+ */
41
+ public function GetName() {
42
+ return __( 'DB & Integrations &#8682;', 'wp-security-audit-log' );
43
+ }
44
+
45
+ /**
46
+ * Method: Get View Weight.
47
+ */
48
+ public function GetWeight() {
49
+ return 10;
50
+ }
51
+
52
+ /**
53
+ * Method: Get View Header.
54
+ */
55
+ public function Header() {
56
+ // Extension Page CSS.
57
+ wp_enqueue_style(
58
+ 'extensions',
59
+ $this->_plugin->GetBaseUrl() . '/css/extensions.css',
60
+ array(),
61
+ filemtime( $this->_plugin->GetBaseDir() . '/css/extensions.css' )
62
+ );
63
+
64
+ // Swipebox CSS.
65
+ wp_enqueue_style(
66
+ 'wsal-swipebox-css',
67
+ $this->_plugin->GetBaseUrl() . '/css/swipebox.min.css',
68
+ array(),
69
+ filemtime( $this->_plugin->GetBaseDir() . '/css/swipebox.min.css' )
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Method: Get View Footer.
75
+ */
76
+ public function Footer() {
77
+ // jQuery.
78
+ wp_enqueue_script( 'jquery' );
79
+
80
+ // Swipebox JS.
81
+ wp_register_script(
82
+ 'wsal-swipebox-js',
83
+ $this->_plugin->GetBaseUrl() . '/js/jquery.swipebox.min.js',
84
+ array( 'jquery' ),
85
+ filemtime( $this->_plugin->GetBaseDir() . '/js/jquery.swipebox.min.js' )
86
+ );
87
+ wp_enqueue_script( 'wsal-swipebox-js' );
88
+
89
+ // Extensions JS.
90
+ wp_register_script(
91
+ 'wsal-extensions-js',
92
+ $this->_plugin->GetBaseUrl() . '/js/extensions.js',
93
+ array( 'wsal-swipebox-js' ),
94
+ filemtime( $this->_plugin->GetBaseDir() . '/js/extensions.js' )
95
+ );
96
+ wp_enqueue_script( 'wsal-extensions-js' );
97
+ }
98
+
99
+ /**
100
+ * Method: Get View.
101
+ */
102
+ public function Render() {
103
+ ?>
104
+ <div class="wrap-advertising-page-single">
105
+ <div class="wsal-row">
106
+ <div class="wsal-col">
107
+ <div class="icon" style='background-image:url("<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/database.jpg");'></div>
108
+ </div>
109
+ <!-- /.wsal-col -->
110
+
111
+ <div class="wsal-col">
112
+ <h3><?php esc_html_e( 'External DB', 'wp-security-audit-log' ); ?></h3>
113
+ <p>
114
+ <?php esc_html_e( 'Upgrade to Premium to:', 'wp-security-audit-log' ); ?>
115
+ </p>
116
+ <p>
117
+ <ul class="wsal-features-list">
118
+ <li><?php esc_html_e( 'Move the audit log to an external database for improved security & performance,', 'wp-security-audit-log' ); ?></li>
119
+ <li><?php esc_html_e( 'Centralize the audit log in your centralized logging system,', 'wp-security-audit-log' ); ?></li>
120
+ <li><?php esc_html_e( 'Mirror the audit trail to Syslog, Papertrail etc,', 'wp-security-audit-log' ); ?></li>
121
+ <li><?php esc_html_e( 'Configure archiving rules to archive old alerts in an archiving database,', 'wp-security-audit-log' ); ?></li>
122
+ <li><?php esc_html_e( '& more.', 'wp-security-audit-log' ); ?></li>
123
+ </ul>
124
+ </p>
125
+ <?php
126
+ $buy_now = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
127
+ $more_info = add_query_arg(
128
+ array(
129
+ 'utm_source' => 'plugin',
130
+ 'utm_medium' => 'page',
131
+ 'utm_content' => 'db+more+info',
132
+ 'utm_campaign' => 'upgrade+premium',
133
+ ),
134
+ 'https://www.wpsecurityauditlog.com/premium-features/'
135
+ );
136
+ ?>
137
+ <p>
138
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $buy_now ); ?>"><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></a>
139
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $more_info ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'wp-security-audit-log' ); ?></a>
140
+ </p>
141
+ </div>
142
+ <!-- /.wsal-col -->
143
+ </div>
144
+ <!-- /.wsal-row -->
145
+
146
+ <div class="wsal-row">
147
+ <div class="wsal-col">
148
+ <h3><?php esc_html_e( 'Screenshots', 'wp-security-audit-log' ); ?></h3>
149
 
150
+ <p>
151
+ <ul class="wsal-features-list">
152
+ <li>
153
+ <?php esc_html_e( 'Configure an external database so the WordPress audit trail is stored on it instead of the WordPress database.', 'wp-security-audit-log' ); ?><br />
154
+ <a class="swipebox" title="<?php esc_attr_e( 'Configure an external database so the WordPress audit trail is stored on it instead of the WordPress database.', 'wp-security-audit-log' ); ?>"
155
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/external-db/external_database_1.png">
156
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/external-db/external_database_1.png">
157
+ </a>
158
+ </li>
159
+ <li>
160
+ <?php esc_html_e( 'Configure mirroring to keep a secondary copy of the WordPress audit trail on Syslog, Papertrail etc.', 'wp-security-audit-log' ); ?><br />
161
+ <a class="swipebox" title="<?php esc_attr_e( 'Configure mirroring to keep a secondary copy of the WordPress audit trail on Syslog, Papertrail etc.', 'wp-security-audit-log' ); ?>"
162
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/external-db/extermal_database_mirroring_options.png">
163
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/external-db/extermal_database_mirroring_options.png">
164
+ </a>
165
+ </li>
166
+ </ul>
167
+ </p>
168
+ </div>
169
+ </div>
170
+ <!-- /.wsal-row -->
171
+ </div>
172
+ <?php
173
+ }
 
 
 
 
 
 
 
 
 
 
174
  }
classes/Views/Help.php CHANGED
@@ -1,89 +1,192 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
 
5
  * Help Page.
6
- * - Plugin support
 
7
  * - Plugin Documentation
 
 
8
  */
9
- class WSAL_Views_Help extends WSAL_AbstractView
10
- {
11
- public function GetTitle()
12
- {
13
- return __('Help', 'wp-security-audit-log');
14
- }
15
-
16
- public function GetIcon()
17
- {
18
- return 'dashicons-sos';
19
- }
20
-
21
- public function GetName()
22
- {
23
- return __('Help', 'wp-security-audit-log');
24
- }
25
-
26
- public function GetWeight()
27
- {
28
- return 5;
29
- }
30
-
31
- public function Render()
32
- {
33
- ?><div class="metabox-holder" style="position: relative;">
34
-
35
- <div class="postbox" style="margin-right: 270px;">
36
- <div class="inside">
37
- <div class="activity-block">
38
- <h2><?php _e('Plugin Support', 'wp-security-audit-log'); ?></h2>
39
- <p>
40
- <?php _e('Have you encountered or noticed any issues while using WP Security Audit Log plugin?', 'wp-security-audit-log'); ?>
41
- <?php _e('Or you want to report something to us? Click any of the options below to post on the plugin\'s forum or contact our support directly.', 'wp-security-audit-log'); ?>
42
- </p><p>
43
- <a class="button" href="https://wordpress.org/support/plugin/wp-security-audit-log" target="_blank"><?php _e('Free Support Forum', 'wp-security-audit-log'); ?></a>
44
- &nbsp;&nbsp;&nbsp;&nbsp;
45
- <a class="button" href="http://www.wpsecurityauditlog.com/contact/" target="_blank"><?php _e('Free Support Email', 'wp-security-audit-log'); ?></a>
46
- </p>
47
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- <div class="activity-block">
50
- <h2><?php _e('Plugin Documentation', 'wp-security-audit-log'); ?></h2>
51
- <p>
52
- <?php _e('For more detailed information about WP Security Audit Log you can visit the plugin website.', 'wp-security-audit-log'); ?>
53
- <?php _e('You can also visit the official list of WordPress Security Alerts for more information about all of the WordPress activity and changes you can monitor with WP Security Audit Log.', 'wp-security-audit-log'); ?>
54
- </p><p>
55
- <a class="button" href="http://www.wpsecurityauditlog.com/?utm_source=plugin&utm_medium=helppage&utm_campaign=support" target="_blank"><?php _e('Plugin Website', 'wp-security-audit-log'); ?></a>
56
- &nbsp;&nbsp;&nbsp;&nbsp;
57
- <a class="button" href="https://www.wpsecurityauditlog.com/documentation/?utm_source=plugin&utm_medium=helppage&utm_campaign=support" target="_blank"><?php _e('Plugin Documenation', 'wp-security-audit-log'); ?></a>
58
- &nbsp;&nbsp;&nbsp;&nbsp;
59
- <a class="button" href="https://www.wpsecurityauditlog.com/documentation/frequently-asked-questions-faqs/?utm_source=plugin&utm_medium=helppage&utm_campaign=support" target="_blank"><?php _e('FAQs', 'wp-security-audit-log'); ?></a>
60
- &nbsp;&nbsp;&nbsp;&nbsp;
61
- <a class="button" href="http://www.wpsecurityauditlog.com/documentation/list-monitoring-wordpress-security-alerts-audit-log/?utm_source=plugin&utm_medium=helppage&utm_campaign=support" target="_blank"><?php _e('List of WordPress Security Alerts', 'wp-security-audit-log'); ?></a>
62
- </p>
63
- </div>
64
 
65
- <div class="">
66
- <h2><?php _e('Keep Yourself Up-to-Date with WordPress Security', 'wp-security-audit-log'); ?></h2>
67
- <p>
68
- <?php _e('Keep yourself informed with what is happening in the WordPress security ecosystem, which are the new vulnerabilities, which plugins you need to update and what are the latest WordPress security hacks so you can stay one step ahead of the hackers.', 'wp-security-audit-log'); ?>
69
- </p>
70
- <a class="button" href="http://www.wpwhitesecurity.com/blog/" target="_blank"><?php _e('Read the WP White Security Blog', 'wp-security-audit-log'); ?></a>
71
- &nbsp;&nbsp;&nbsp;&nbsp;
72
- <a class="button" href="http://www.wpsecuritybloggers.com" target="_blank"><?php _e('Subscribe to WP Security Bloggers (An Aggregate of WordPress Security Blogs)', 'wp-security-audit-log'); ?></a>
73
- </div>
74
- </div>
75
- </div>
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- <div style="position: absolute; right: 70px; width: 180px; top: 10px;">
78
- <div class="postbox">
79
- <h3 class="hndl"><span><?php _e('WP Security Audit Log in your Language!', 'wp-security-audit-log'); ?></span></h3>
80
- <div class="inside">
81
- <?php _e('If you are interested in translating our plugin please drop us an email on', 'wp-security-audit-log'); ?>
82
- <a href="mailto:plugins@wpwhitesecurity.com">plugins@wpwhitesecurity.com</a>.
83
- </div>
84
- </div>
85
- </div>
86
-
87
- </div><?php
88
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
1
  <?php
2
  /**
3
+ * View: Help
4
+ *
5
+ * WSAL help page.
6
  *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Help Page.
18
+ *
19
+ * - Plugin Support
20
  * - Plugin Documentation
21
+ *
22
+ * @package Wsal
23
  */
24
+ class WSAL_Views_Help extends WSAL_AbstractView {
25
+
26
+ /**
27
+ * Method: Get View Title.
28
+ */
29
+ public function GetTitle() {
30
+ return __( 'Help', 'wp-security-audit-log' );
31
+ }
32
+
33
+ /**
34
+ * Method: Get View Icon.
35
+ */
36
+ public function GetIcon() {
37
+ return 'dashicons-sos';
38
+ }
39
+
40
+ /**
41
+ * Method: Get View Name.
42
+ */
43
+ public function GetName() {
44
+ return __( 'Help', 'wp-security-audit-log' );
45
+ }
46
+
47
+ /**
48
+ * Method: Get View Weight.
49
+ */
50
+ public function GetWeight() {
51
+ return 14;
52
+ }
53
+
54
+ /**
55
+ * Method: Get View Header.
56
+ */
57
+ public function Header() {
58
+ wp_enqueue_style(
59
+ 'extensions',
60
+ $this->_plugin->GetBaseUrl() . '/css/extensions.css',
61
+ array(),
62
+ filemtime( $this->_plugin->GetBaseDir() . '/css/extensions.css' )
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Method: Get View.
68
+ */
69
+ public function Render() {
70
+ ?>
71
+ <div class="metabox-holder" style="position: relative;">
72
+ <div class="postbox">
73
+ <div class="inside wsal-block">
74
+ <div class="activity-block">
75
+ <h2><?php esc_html_e( 'Getting Started', 'wp-security-audit-log' ); ?></h2>
76
+ <p>
77
+ <?php esc_html_e( 'Getting started with WP Security Audit Log is really easy; once the plugin is installed it will automatically keep a log of everything that is happening on your website and you do not need to do anything. Watch the video below for a quick overview of the plugin.', 'wp-security-audit-log' ); ?>
78
+ </p>
79
+ <p>
80
+ <iframe class="wsal-youtube-embed" width="560" height="315" src="https://www.youtube.com/embed/1nopATCS-CQ?rel=0" frameborder="0" allowfullscreen></iframe>
81
+ </p>
82
+ </div>
83
+ <!-- /.activity-block -->
84
+
85
+ <div class="activity-block">
86
+ <h2><?php esc_html_e( 'Plugin Support', 'wp-security-audit-log' ); ?></h2>
87
+ <p>
88
+ <?php esc_html_e( 'Have you encountered or noticed any issues while using WP Security Audit Log plugin?', 'wp-security-audit-log' ); ?>
89
+ <?php esc_html_e( 'Or you want to report something to us? Click any of the options below to post on the plugin\'s forum or contact our support directly.', 'wp-security-audit-log' ); ?>
90
+ </p><p>
91
+ <a class="button" href="https://wordpress.org/support/plugin/wp-security-audit-log" target="_blank"><?php esc_html_e( 'Free Support Forum', 'wp-security-audit-log' ); ?></a>
92
+ &nbsp;&nbsp;&nbsp;&nbsp;
93
+ <a class="button" href="http://www.wpsecurityauditlog.com/contact/" target="_blank"><?php esc_html_e( 'Free Support Email', 'wp-security-audit-log' ); ?></a>
94
+ </p>
95
+ </div>
96
+ <!-- /.activity-block -->
97
 
98
+ <div class="activity-block">
99
+ <h2><?php esc_html_e( 'Plugin Documentation', 'wp-security-audit-log' ); ?></h2>
100
+ <p>
101
+ <?php esc_html_e( 'For more technical information about the WP Security Audit Log plugin please visit the plugin’s knowledge base.', 'wp-security-audit-log' ); ?>
102
+ <?php esc_html_e( 'Refer to the list of WordPress security alerts for a complete list of Alerts and IDs that the plugin uses to keep a log of all the changes in the WordPress audit log.', 'wp-security-audit-log' ); ?>
103
+ </p><p>
104
+ <a class="button" href="http://www.wpsecurityauditlog.com/?utm_source=plugin&amp;utm_medium=helppage&amp;utm_campaign=support" target="_blank"><?php esc_html_e( 'Plugin Website', 'wp-security-audit-log' ); ?></a>
105
+ &nbsp;&nbsp;&nbsp;&nbsp;
106
+ <a class="button" href="https://www.wpsecurityauditlog.com/support-documentation/?utm_source=plugin&amp;utm_medium=helppage&amp;utm_campaign=support" target="_blank"><?php esc_html_e( 'Knowledge Base', 'wp-security-audit-log' ); ?></a>
107
+ &nbsp;&nbsp;&nbsp;&nbsp;
108
+ <a class="button" href="http://www.wpsecurityauditlog.com/documentation/list-monitoring-wordpress-security-alerts-audit-log/?utm_source=plugin&amp;utm_medium=helppage&amp;utm_campaign=support" target="_blank"><?php esc_html_e( 'List of WordPress Security Alerts', 'wp-security-audit-log' ); ?></a>
109
+ </p>
110
+ </div>
111
+ <!-- /.activity-block -->
 
112
 
113
+ <div class="activity-block">
114
+ <h2><?php esc_html_e( 'Rate WP Security Audit Log', 'wp-security-audit-log' ); ?></h2>
115
+ <p>
116
+ <?php esc_html_e( 'We work really hard to deliver a plugin that enables you to keep a record of all the changes that are happening on your WordPress.', 'wp-security-audit-log' ); ?>
117
+ <?php esc_html_e( 'It takes thousands of man-hours every year and endless amount of dedication to research, develop and maintain the free edition of WP Security Audit Log.', 'wp-security-audit-log' ); ?>
118
+ <?php esc_html_e( 'Therefore if you like what you see, and find WP Security Audit Log useful we ask you nothing more than to please rate our plugin.', 'wp-security-audit-log' ); ?>
119
+ <?php esc_html_e( 'We appreciate every star!', 'wp-security-audit-log' ); ?>
120
+ </p>
121
+ <p>
122
+ <a class="rating-link" href="https://en-gb.wordpress.org/plugins/wp-security-audit-log/#reviews" target="_blank">
123
+ <span class="dashicons dashicons-star-filled"></span>
124
+ <span class="dashicons dashicons-star-filled"></span>
125
+ <span class="dashicons dashicons-star-filled"></span>
126
+ <span class="dashicons dashicons-star-filled"></span>
127
+ <span class="dashicons dashicons-star-filled"></span>
128
+ </a>
129
+ <a class="button" href="https://en-gb.wordpress.org/plugins/wp-security-audit-log/#reviews" target="_blank"><?php esc_html_e( 'Rate Plugin', 'wp-security-audit-log' ); ?></a>
130
+ </p>
131
+ </div>
132
+ <!-- /.activity-block -->
133
+ </div>
134
+ </div>
135
 
136
+ <?php
137
+ $is_current_view = $this->_plugin->views->GetActiveView() == $this;
138
+ // Check if any of the extensions is activated.
139
+ if ( wsal_freemius()->is_not_paying() ) :
140
+ if ( current_user_can( 'manage_options' ) && $is_current_view ) :
141
+ ?>
142
+ <div class="wsal-sidebar-advert">
143
+ <div class="postbox">
144
+ <h3 class="hndl"><span><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></span></h3>
145
+ <div class="inside">
146
+ <ul class="wsal-features-list">
147
+ <li>
148
+ <?php esc_html_e( 'See who is logged in', 'wp-security-audit-log' ); ?><br />
149
+ <?php esc_html_e( 'And remotely terminate sessions', 'wp-security-audit-log' ); ?>
150
+ </li>
151
+ <li>
152
+ <?php esc_html_e( 'Generate reports', 'wp-security-audit-log' ); ?><br />
153
+ <?php esc_html_e( 'Or configure automated email reports', 'wp-security-audit-log' ); ?>
154
+ </li>
155
+ <li>
156
+ <?php esc_html_e( 'Configure email notifications', 'wp-security-audit-log' ); ?><br />
157
+ <?php esc_html_e( 'Get instantly notified of important changes', 'wp-security-audit-log' ); ?>
158
+ </li>
159
+ <li>
160
+ <?php esc_html_e( 'Add Search', 'wp-security-audit-log' ); ?><br />
161
+ <?php esc_html_e( 'Easily track down suspicious behaviour', 'wp-security-audit-log' ); ?>
162
+ </li>
163
+ <li>
164
+ <?php esc_html_e( 'Integrate & Centralise', 'wp-security-audit-log' ); ?><br />
165
+ <?php esc_html_e( 'Export the logs to your centralised logging system', 'wp-security-audit-log' ); ?>
166
+ </li>
167
+ </ul>
168
+ <?php
169
+ $buy_now = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
170
+ $more_info = add_query_arg(
171
+ array(
172
+ 'utm_source' => 'plugin',
173
+ 'utm_medium' => 'page',
174
+ 'utm_content' => 'update+more+info',
175
+ 'utm_campaign' => 'upgrade+premium',
176
+ ),
177
+ 'https://www.wpsecurityauditlog.com/premium-features/'
178
+ );
179
+ ?>
180
+ <p>
181
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $buy_now ); ?>"><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></a>
182
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $more_info ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'wp-security-audit-log' ); ?></a>
183
+ </p>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ <?php endif; ?>
188
+ <?php endif; ?>
189
+ </div>
190
+ <?php
191
+ }
192
  }
classes/Views/Licensing.php CHANGED
@@ -1,100 +1,163 @@
1
  <?php
2
  /**
 
 
 
 
3
  * @package Wsal
 
 
 
 
 
 
 
 
 
4
  *
5
  * Licensing Page for all the Add-Ons enabled.
 
 
6
  */
7
- class WSAL_Views_Licensing extends WSAL_AbstractView
8
- {
9
- public function GetTitle()
10
- {
11
- return __('Licensing', 'wp-security-audit-log');
12
- }
13
-
14
- public function GetIcon()
15
- {
16
- return 'dashicons-cart';
17
- }
18
-
19
- public function GetName()
20
- {
21
- return __('Licensing', 'wp-security-audit-log');
22
- }
23
-
24
- public function GetWeight()
25
- {
26
- return 4;
27
- }
28
-
29
- public function IsAccessible()
30
- {
31
- return !!$this->_plugin->licensing->CountPlugins();
32
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- protected function Save()
35
- {
36
- $this->_plugin->settings->ClearLicenses();
37
- if (isset($_REQUEST['license'])) {
38
- foreach ($_REQUEST['license'] as $name => $key) {
39
- $this->_plugin->licensing->ActivateLicense($name, $key);
40
- }
41
- }
42
- }
43
-
44
- public function Render()
45
- {
46
- if (!$this->_plugin->settings->CurrentUserCan('edit')) {
47
- wp_die(__('You do not have sufficient permissions to access this page.', 'wp-security-audit-log'));
48
- }
49
- if (isset($_POST['submit'])) {
50
- try {
51
- $this->Save();
52
- ?><div class="updated"><p><?php _e('Settings have been saved.', 'wp-security-audit-log'); ?></p></div><?php
53
- } catch (Exception $ex) {
54
- ?><div class="error"><p><?php _e('Error: ', 'wp-security-audit-log'); ?><?php echo $ex->getMessage(); ?></p></div><?php
55
- }
56
- }
57
- ?><form id="audit-log-licensing" method="post">
58
- <input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
59
-
60
- <table class="wp-list-table widefat fixed">
61
- <thead>
62
- <tr><th>Plugin</th><th>License</th><th></th></tr>
63
- </thead><tbody>
64
- <?php $counter = 0; ?>
65
- <?php foreach ($this->_plugin->licensing->Plugins() as $name => $plugin) { ?>
66
- <?php $licenseKey = trim($this->_plugin->settings->GetLicenseKey($name)); ?>
67
- <?php $licenseStatus = trim($this->_plugin->settings->GetLicenseStatus($name)); ?>
68
- <?php $licenseErrors = trim($this->_plugin->settings->GetLicenseErrors($name)); ?>
69
- <tr class="<?php echo ($counter++ % 2 === 0) ? 'alternate' : ''; ?>">
70
- <td>
71
- <a href="<?php echo esc_attr($plugin['PluginData']['PluginURI']); ?>" target="_blank">
72
- <?php echo esc_html($plugin['PluginData']['Name']); ?>
73
- </a><br/><small><b>
74
- <?php _e('Version', 'wp-security-audit-log'); ?>
75
- <?php echo esc_html($plugin['PluginData']['Version']); ?>
76
- </b></small>
77
- </td><td>
78
- <input type="text" style="width: 360px; margin: 6px 0;"
79
- name="license[<?php echo esc_attr($name); ?>]"
80
- value="<?php echo esc_attr($licenseKey); ?>"/>
81
- </td><td style="vertical-align: middle;">
82
- <?php if ($licenseKey) { ?>
83
- <?php if ($licenseStatus === 'valid') { ?>
84
- <?php _e('Active', 'wp-security-audit-log'); ?>
85
- <?php } else { ?>
86
- <?php _e('Inactive', 'wp-security-audit-log'); ?><br/>
87
- <small><?php echo esc_html($licenseErrors); ?></small>
88
- <?php } ?>
89
- <?php } ?>
90
- </td>
91
- </tr>
92
- <?php } ?>
93
- </tbody><tfoot>
94
- <tr><th>Plugin</th><th>License</th><th></th></tr>
95
- </tfoot>
96
- </table>
97
- <?php submit_button(); ?>
98
- </form><?php
99
- }
 
 
 
 
100
  }
1
  <?php
2
  /**
3
+ * View: Licensing
4
+ *
5
+ * WSAL licensing page.
6
+ *
7
  * @package Wsal
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * Class: Licensing View.
17
  *
18
  * Licensing Page for all the Add-Ons enabled.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Views_Licensing extends WSAL_AbstractView {
23
+
24
+ /**
25
+ * Method: Get View Title.
26
+ */
27
+ public function GetTitle() {
28
+ return __( 'Licensing', 'wp-security-audit-log' );
29
+ }
30
+
31
+ /**
32
+ * Method: Get View Icon.
33
+ */
34
+ public function GetIcon() {
35
+ return 'dashicons-cart';
36
+ }
37
+
38
+ /**
39
+ * Method: Get View Name.
40
+ */
41
+ public function GetName() {
42
+ return __( 'Licensing', 'wp-security-audit-log' );
43
+ }
44
+
45
+ /**
46
+ * Method: Get View Weight.
47
+ */
48
+ public function GetWeight() {
49
+ return 13;
50
+ }
51
+
52
+ /**
53
+ * Check if the view is accessible.
54
+ */
55
+ public function IsAccessible() {
56
+ return ! ! $this->_plugin->licensing->CountPlugins();
57
+ }
58
+
59
+ /**
60
+ * Method: Save.
61
+ */
62
+ protected function Save() {
63
+ // Clear licenses.
64
+ $this->_plugin->settings->ClearLicenses();
65
+
66
+ // Filter $_POST array for security.
67
+ $post_array = filter_input_array( INPUT_POST );
68
+
69
+ // Save and activate license.
70
+ if ( isset( $post_array['license'] ) ) {
71
+ foreach ( $post_array['license'] as $name => $key ) {
72
+ $this->_plugin->licensing->ActivateLicense( $name, $key );
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Method: Get View.
79
+ */
80
+ public function Render() {
81
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'edit' ) ) {
82
+ wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-security-audit-log' ) );
83
+ }
84
+
85
+ // Filter $_POST array for security.
86
+ $post_array = filter_input_array( INPUT_POST );
87
+
88
+ // Verify nonce for security.
89
+ if ( isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'wsal-licensing' ) ) {
90
+ wp_die( esc_html__( 'Nonce verification failed.', 'wp-security-audit-log' ) );
91
+ }
92
 
93
+ if ( isset( $post_array['submit'] ) ) {
94
+ try {
95
+ $this->Save();
96
+ ?><div class="updated"><p><?php esc_html_e( 'Settings have been saved.', 'wp-security-audit-log' ); ?></p></div>
97
+ <?php
98
+ } catch ( Exception $ex ) {
99
+ ?>
100
+ <div class="error">
101
+ <p><?php esc_html_e( 'Error: ', 'wp-security-audit-log' ); ?><?php echo esc_html( $ex->getMessage() ); ?></p>
102
+ </div>
103
+ <?php
104
+ }
105
+ }
106
+ ?>
107
+ <form id="audit-log-licensing" method="post">
108
+ <input type="hidden" name="page" value="<?php echo filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING ); ?>" />
109
+ <?php wp_nonce_field( 'wsal-licensing' ); ?>
110
+ <table class="wp-list-table widefat fixed">
111
+ <thead>
112
+ <tr>
113
+ <th><?php esc_html_e( 'Plugin', 'wp-security-audit-log' ); ?></th>
114
+ <th><?php esc_html_e( 'License', 'wp-security-audit-log' ); ?></th>
115
+ <th></th>
116
+ </tr>
117
+ </thead>
118
+ <tbody>
119
+ <?php $counter = 0; ?>
120
+ <?php foreach ( $this->_plugin->licensing->Plugins() as $name => $plugin ) : ?>
121
+ <?php $license_key = trim( $this->_plugin->settings->GetLicenseKey( $name ) ); ?>
122
+ <?php $license_status = trim( $this->_plugin->settings->GetLicenseStatus( $name ) ); ?>
123
+ <?php $license_errors = trim( $this->_plugin->settings->GetLicenseErrors( $name ) ); ?>
124
+ <tr class="<?php echo ( 0 === $counter++ % 2 ) ? 'alternate' : ''; ?>">
125
+ <td>
126
+ <a href="<?php echo esc_attr( $plugin['PluginData']['PluginURI'] ); ?>" target="_blank">
127
+ <?php echo esc_html( $plugin['PluginData']['Name'] ); ?>
128
+ </a><br/><small><b>
129
+ <?php esc_html_e( 'Version', 'wp-security-audit-log' ); ?>
130
+ <?php echo esc_html( $plugin['PluginData']['Version'] ); ?>
131
+ </b></small>
132
+ </td>
133
+ <td>
134
+ <input type="text" style="width: 100%; margin: 6px 0;"
135
+ name="license[<?php echo esc_attr( $name ); ?>]"
136
+ value="<?php echo esc_attr( $license_key ); ?>"/>
137
+ </td>
138
+ <td style="vertical-align: middle;">
139
+ <?php if ( $license_key ) : ?>
140
+ <?php if ( 'valid' === $license_status ) : ?>
141
+ <?php esc_html_e( 'Active', 'wp-security-audit-log' ); ?>
142
+ <?php else : ?>
143
+ <?php esc_html_e( 'Inactive', 'wp-security-audit-log' ); ?><br/>
144
+ <small><?php echo esc_html( $license_errors ); ?></small>
145
+ <?php endif; ?>
146
+ <?php endif; ?>
147
+ </td>
148
+ </tr>
149
+ <?php endforeach; ?>
150
+ </tbody>
151
+ <tfoot>
152
+ <tr>
153
+ <th><?php esc_html_e( 'Plugin', 'wp-security-audit-log' ); ?></th>
154
+ <th><?php esc_html_e( 'License', 'wp-security-audit-log' ); ?></th>
155
+ <th></th>
156
+ </tr>
157
+ </tfoot>
158
+ </table>
159
+ <?php submit_button(); ?>
160
+ </form>
161
+ <?php
162
+ }
163
  }
classes/Views/LogInUsers.php CHANGED
@@ -1,64 +1,176 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
 
5
  * User Sessions Management Add-On promo Page.
6
  * Used only if the plugin is not activated.
 
 
7
  */
8
- class WSAL_Views_LogInUsers extends WSAL_AbstractView
9
- {
10
- public function GetTitle()
11
- {
12
- return __('User Sessions Management Add-On', 'wp-security-audit-log');
13
- }
14
-
15
- public function GetIcon()
16
- {
17
- return 'dashicons-external';
18
- }
19
-
20
- public function GetName()
21
- {
22
- return __('Logged In Users', 'wp-security-audit-log');
23
- }
24
-
25
- public function GetWeight()
26
- {
27
- return 8;
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- public function Header()
31
- {
32
- wp_enqueue_style(
33
- 'extensions',
34
- $this->_plugin->GetBaseUrl() . '/css/extensions.css',
35
- array(),
36
- filemtime($this->_plugin->GetBaseDir() . '/css/extensions.css')
37
- );
38
- }
39
-
40
- public function Render()
41
- {
42
- ?>
43
- <div class="wrap-advertising-page-single">
44
- <div class="icon" style='background-image:url("<?=$this->_plugin->GetBaseUrl();?>/img/monitoring.jpg");'></div>
45
- <h3><?php _e('Users login and Management', 'wp-security-audit-log'); ?></h3>
46
- <p>
47
- <?php _e('This premium add-on allows you to see who is logged in to your WordPress,<br> block multiple same-user WordPress sessions and more.', 'wp-security-audit-log'); ?>
48
- </p>
49
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/user-sessions-management-wp-security-audit-log/?utm_source=plugin&utm_medium=loginspage&utm_campaign=logins'; ?>
50
- <p>
51
- <a class="button-primary" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Learn More', 'wp-security-audit-log'); ?></a>
52
- </p>
53
- <div class="clear"></div>
54
- <p>
55
- <span class="description">
56
- <strong><span class="text-red">70% Off</span> when you purchase this add-on as part of the All Add-On bundle.</strong>
57
- </span>
58
- </p>
59
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=alladdons'; ?>
60
- <a class="button-blue" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Buy all Add-Ons Bundle', 'wp-security-audit-log'); ?></a>
61
- </div>
62
- <?php
63
- }
64
  }
1
  <?php
2
  /**
3
+ * View: Users Sessions Page
4
+ *
5
+ * WSAL users sessions page.
6
  *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * User Sessions Management Add-On promo Page.
18
  * Used only if the plugin is not activated.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Views_LogInUsers extends WSAL_AbstractView {
23
+
24
+ /**
25
+ * Method: Get View Title.
26
+ */
27
+ public function GetTitle() {
28
+ return __( 'User Sessions Management Add-On', 'wp-security-audit-log' );
29
+ }
30
+
31
+ /**
32
+ * Method: Get View Icon.
33
+ */
34
+ public function GetIcon() {
35
+ return 'dashicons-external';
36
+ }
37
+
38
+ /**
39
+ * Method: Get View Name.
40
+ */
41
+ public function GetName() {
42
+ return __( 'Logged In Users &#8682;', 'wp-security-audit-log' );
43
+ }
44
+
45
+ /**
46
+ * Method: Get View Weight.
47
+ */
48
+ public function GetWeight() {
49
+ return 7;
50
+ }
51
+
52
+ /**
53
+ * Method: Get View Header.
54
+ */
55
+ public function Header() {
56
+ // Extension Page CSS.
57
+ wp_enqueue_style(
58
+ 'extensions',
59
+ $this->_plugin->GetBaseUrl() . '/css/extensions.css',
60
+ array(),
61
+ filemtime( $this->_plugin->GetBaseDir() . '/css/extensions.css' )
62
+ );
63
+
64
+ // Swipebox CSS.
65
+ wp_enqueue_style(
66
+ 'wsal-swipebox-css',
67
+ $this->_plugin->GetBaseUrl() . '/css/swipebox.min.css',
68
+ array(),
69
+ filemtime( $this->_plugin->GetBaseDir() . '/css/swipebox.min.css' )
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Method: Get View Footer.
75
+ */
76
+ public function Footer() {
77
+ // jQuery.
78
+ wp_enqueue_script( 'jquery' );
79
+
80
+ // Swipebox JS.
81
+ wp_register_script(
82
+ 'wsal-swipebox-js',
83
+ $this->_plugin->GetBaseUrl() . '/js/jquery.swipebox.min.js',
84
+ array( 'jquery' ),
85
+ filemtime( $this->_plugin->GetBaseDir() . '/js/jquery.swipebox.min.js' )
86
+ );
87
+ wp_enqueue_script( 'wsal-swipebox-js' );
88
+
89
+ // Extensions JS.
90
+ wp_register_script(
91
+ 'wsal-extensions-js',
92
+ $this->_plugin->GetBaseUrl() . '/js/extensions.js',
93
+ array( 'wsal-swipebox-js' ),
94
+ filemtime( $this->_plugin->GetBaseDir() . '/js/extensions.js' )
95
+ );
96
+ wp_enqueue_script( 'wsal-extensions-js' );
97
+ }
98
+
99
+ /**
100
+ * Method: Get View.
101
+ */
102
+ public function Render() {
103
+ ?>
104
+ <div class="wrap-advertising-page-single">
105
+ <div class="wsal-row">
106
+ <div class="wsal-col">
107
+ <div class="icon" style='background-image:url("<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/monitoring.jpg");'></div>
108
+ </div>
109
+ <!-- /.wsal-col -->
110
+
111
+ <div class="wsal-col">
112
+ <h3><?php esc_html_e( 'Users Login and Management', 'wp-security-audit-log' ); ?></h3>
113
+ <p>
114
+ <?php esc_html_e( 'Upgrade to Premium to:', 'wp-security-audit-log' ); ?>
115
+ </p>
116
+ <p>
117
+ <ul class="wsal-features-list">
118
+ <li><?php esc_html_e( 'See who is logged in to your WordPress website,', 'wp-security-audit-log' ); ?></li>
119
+ <li><?php esc_html_e( 'When they logged in and from where,', 'wp-security-audit-log' ); ?></li>
120
+ <li><?php esc_html_e( 'The last change they did on your WordPress website,', 'wp-security-audit-log' ); ?></li>
121
+ <li><?php esc_html_e( 'Terminate their session with just a click of a button,', 'wp-security-audit-log' ); ?></li>
122
+ <li><?php esc_html_e( 'Block multiple sessions for the same user,', 'wp-security-audit-log' ); ?></li>
123
+ <li><?php esc_html_e( 'Get alerted when there are multiple sessions with the same username,', 'wp-security-audit-log' ); ?></li>
124
+ <li><?php esc_html_e( '& more.', 'wp-security-audit-log' ); ?></li>
125
+ </ul>
126
+ </p>
127
+ <?php
128
+ $buy_now = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
129
+ $more_info = add_query_arg(
130
+ array(
131
+ 'utm_source' => 'plugin',
132
+ 'utm_medium' => 'page',
133
+ 'utm_content' => 'users+sessions+more+info',
134
+ 'utm_campaign' => 'upgrade+premium',
135
+ ),
136
+ 'https://www.wpsecurityauditlog.com/premium-features/'
137
+ );
138
+ ?>
139
+ <p>
140
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $buy_now ); ?>"><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></a>
141
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $more_info ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'wp-security-audit-log' ); ?></a>
142
+ </p>
143
+ </div>
144
+ <!-- /.wsal-col -->
145
+ </div>
146
+ <!-- /.wsal-row -->
147
+
148
+ <div class="wsal-row">
149
+ <div class="wsal-col">
150
+ <h3><?php esc_html_e( 'Screenshots', 'wp-security-audit-log' ); ?></h3>
151
 
152
+ <p>
153
+ <ul class="wsal-features-list">
154
+ <li>
155
+ <?php esc_html_e( 'See who is logged in to your WordPress website and WordPress multisite network.', 'wp-security-audit-log' ); ?><br />
156
+ <a class="swipebox" title="<?php esc_attr_e( 'See who is logged in to your WordPress website and WordPress multisite network.', 'wp-security-audit-log' ); ?>"
157
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/users-sessions-management/logged_in_users.png">
158
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/users-sessions-management/logged_in_users.png">
159
+ </a>
160
+ </li>
161
+ <li>
162
+ <?php esc_html_e( 'Block multiple sessions for the same user and configure related email notifications.', 'wp-security-audit-log' ); ?><br />
163
+ <a class="swipebox" title="<?php esc_attr_e( 'Block multiple sessions for the same user and configure related email notifications.', 'wp-security-audit-log' ); ?>"
164
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/users-sessions-management/users_session_management_config.png">
165
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/users-sessions-management/users_session_management_config.png">
166
+ </a>
167
+ </li>
168
+ </ul>
169
+ </p>
170
+ </div>
171
+ </div>
172
+ <!-- /.wsal-row -->
173
+ </div>
174
+ <?php
175
+ }
 
 
 
 
 
 
 
 
 
 
176
  }
classes/Views/Reports.php CHANGED
@@ -1,64 +1,175 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
 
5
  * Reports Add-On promo Page.
6
  * Used only if the plugin is not activated.
 
 
7
  */
8
- class WSAL_Views_Reports extends WSAL_AbstractView
9
- {
10
- public function GetTitle()
11
- {
12
- return __('Reports Add-On', 'wp-security-audit-log');
13
- }
14
-
15
- public function GetIcon()
16
- {
17
- return 'dashicons-external';
18
- }
19
-
20
- public function GetName()
21
- {
22
- return __('Reports', 'wp-security-audit-log');
23
- }
24
-
25
- public function GetWeight()
26
- {
27
- return 9;
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- public function Header()
31
- {
32
- wp_enqueue_style(
33
- 'extensions',
34
- $this->_plugin->GetBaseUrl() . '/css/extensions.css',
35
- array(),
36
- filemtime($this->_plugin->GetBaseDir() . '/css/extensions.css')
37
- );
38
- }
39
-
40
- public function Render()
41
- {
42
- ?>
43
- <div class="wrap-advertising-page-single">
44
- <div class="icon" style='background-image:url("<?=$this->_plugin->GetBaseUrl();?>/img/file.jpg");'></div>
45
- <h3><?php _e('Reports', 'wp-security-audit-log'); ?></h3>
46
- <p>
47
- <?php _e('Generate any type of user and site activity report to keep track of user productivity<br> and meet regulatory compliance requirements. You can also configure automated weekly or monthly email summary reports.', 'wp-security-audit-log'); ?>
48
- </p>
49
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/compliance-reports-add-on-for-wordpress/?utm_source=plugin&utm_medium=reportspage&utm_campaign=reports'; ?>
50
- <p>
51
- <a class="button-primary" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Learn More', 'wp-security-audit-log'); ?></a>
52
- </p>
53
- <div class="clear"></div>
54
- <p>
55
- <span class="description">
56
- <strong><span class="text-red">70% Off</span> when you purchase this add-on as part of the All Add-On bundle.</strong>
57
- </span>
58
- </p>
59
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=alladdons'; ?>
60
- <a class="button-blue" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Buy all Add-Ons Bundle', 'wp-security-audit-log'); ?></a>
61
- </div>
62
- <?php
63
- }
64
  }
1
  <?php
2
  /**
3
+ * View: Reports Page
4
+ *
5
+ * WSAL reports page.
6
  *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Reports Add-On promo Page.
18
  * Used only if the plugin is not activated.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Views_Reports extends WSAL_AbstractView {
23
+
24
+ /**
25
+ * Method: Get View Title.
26
+ */
27
+ public function GetTitle() {
28
+ return __( 'Reports Add-On', 'wp-security-audit-log' );
29
+ }
30
+
31
+ /**
32
+ * Method: Get View Icon.
33
+ */
34
+ public function GetIcon() {
35
+ return 'dashicons-external';
36
+ }
37
+
38
+ /**
39
+ * Method: Get View Name.
40
+ */
41
+ public function GetName() {
42
+ return __( 'Reports &#8682;', 'wp-security-audit-log' );
43
+ }
44
+
45
+ /**
46
+ * Method: Get View Weight.
47
+ */
48
+ public function GetWeight() {
49
+ return 8;
50
+ }
51
+
52
+ /**
53
+ * Method: Get View Header.
54
+ */
55
+ public function Header() {
56
+ // Extension Page CSS.
57
+ wp_enqueue_style(
58
+ 'extensions',
59
+ $this->_plugin->GetBaseUrl() . '/css/extensions.css',
60
+ array(),
61
+ filemtime( $this->_plugin->GetBaseDir() . '/css/extensions.css' )
62
+ );
63
+
64
+ // Swipebox CSS.
65
+ wp_enqueue_style(
66
+ 'wsal-swipebox-css',
67
+ $this->_plugin->GetBaseUrl() . '/css/swipebox.min.css',
68
+ array(),
69
+ filemtime( $this->_plugin->GetBaseDir() . '/css/swipebox.min.css' )
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Method: Get View Footer.
75
+ */
76
+ public function Footer() {
77
+ // jQuery.
78
+ wp_enqueue_script( 'jquery' );
79
+
80
+ // Swipebox JS.
81
+ wp_register_script(
82
+ 'wsal-swipebox-js',
83
+ $this->_plugin->GetBaseUrl() . '/js/jquery.swipebox.min.js',
84
+ array( 'jquery' ),
85
+ filemtime( $this->_plugin->GetBaseDir() . '/js/jquery.swipebox.min.js' )
86
+ );
87
+ wp_enqueue_script( 'wsal-swipebox-js' );
88
+
89
+ // Extensions JS.
90
+ wp_register_script(
91
+ 'wsal-extensions-js',
92
+ $this->_plugin->GetBaseUrl() . '/js/extensions.js',
93
+ array( 'wsal-swipebox-js' ),
94
+ filemtime( $this->_plugin->GetBaseDir() . '/js/extensions.js' )
95
+ );
96
+ wp_enqueue_script( 'wsal-extensions-js' );
97
+ }
98
+
99
+ /**
100
+ * Method: Get View.
101
+ */
102
+ public function Render() {
103
+ ?>
104
+ <div class="wrap-advertising-page-single">
105
+ <div class="wsal-row">
106
+ <div class="wsal-col">
107
+ <div class="icon" style='background-image:url("<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/file.jpg");'></div>
108
+ </div>
109
+ <!-- /.wsal-col -->
110
+
111
+ <div class="wsal-col">
112
+ <h3><?php esc_html_e( 'Reports', 'wp-security-audit-log' ); ?></h3>
113
+ <p>
114
+ <?php esc_html_e( 'Upgrade to Premium to:', 'wp-security-audit-log' ); ?>
115
+ </p>
116
+ <p>
117
+ <ul class="wsal-features-list">
118
+ <li><?php esc_html_e( 'Generate user activity, site (in multisite) and any other type of WordPress reports,', 'wp-security-audit-log' ); ?></li>
119
+ <li><?php esc_html_e( 'Configure automated daily, weekly, monthly & quarterly reports,', 'wp-security-audit-log' ); ?></li>
120
+ <li><?php esc_html_e( 'Receive reports automatically in your email,', 'wp-security-audit-log' ); ?></li>
121
+ <li><?php esc_html_e( 'Generate statistics reports on commonly used IP addresses, views per user, etc,', 'wp-security-audit-log' ); ?></li>
122
+ <li><?php esc_html_e( 'Export reports to HTML and CSV formats,', 'wp-security-audit-log' ); ?></li>
123
+ <li><?php esc_html_e( '& much more.', 'wp-security-audit-log' ); ?></li>
124
+ </ul>
125
+ </p>
126
+ <?php
127
+ $buy_now = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
128
+ $more_info = add_query_arg(
129
+ array(
130
+ 'utm_source' => 'plugin',
131
+ 'utm_medium' => 'page',
132
+ 'utm_content' => 'reports+more+info',
133
+ 'utm_campaign' => 'upgrade+premium',
134
+ ),
135
+ 'https://www.wpsecurityauditlog.com/premium-features/'
136
+ );
137
+ ?>
138
+ <p>
139
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $buy_now ); ?>"><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></a>
140
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $more_info ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'wp-security-audit-log' ); ?></a>
141
+ </p>
142
+ </div>
143
+ <!-- /.wsal-col -->
144
+ </div>
145
+ <!-- /.wsal-row -->
146
+
147
+ <div class="wsal-row">
148
+ <div class="wsal-col">
149
+ <h3><?php esc_html_e( 'Screenshots', 'wp-security-audit-log' ); ?></h3>
150
 
151
+ <p>
152
+ <ul class="wsal-features-list">
153
+ <li>
154
+ <?php esc_html_e( 'Generate any type of report and also configure daily, weekly, monthly and quarterly reports which are automatically sent to you via email.', 'wp-security-audit-log' ); ?><br />
155
+ <a class="swipebox" title="<?php esc_attr_e( 'Generate any type of report and also configure daily, weekly, monthly and quarterly reports which are automatically sent to you via email.', 'wp-security-audit-log' ); ?>"
156
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/reports/reports_1.png">
157
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/reports/reports_1.png">
158
+ </a>
159
+ </li>
160
+ <li>
161
+ <?php esc_html_e( 'Generate statistical reports to get a better overview of what users are doing on your WordPress and WordPress multisite network are doing.', 'wp-security-audit-log' ); ?><br />
162
+ <a class="swipebox" title="<?php esc_attr_e( 'Generate statistical reports to get a better overview of what users are doing on your WordPress and WordPress multisite network are doing.', 'wp-security-audit-log' ); ?>"
163
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/reports/reports_2.png">
164
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/reports/reports_2.png">
165
+ </a>
166
+ </li>
167
+ </ul>
168
+ </p>
169
+ </div>
170
+ </div>
171
+ <!-- /.wsal-row -->
172
+ </div>
173
+ <?php
174
+ }
 
 
 
 
 
 
 
 
 
 
175
  }
classes/Views/Search.php CHANGED
@@ -1,64 +1,168 @@
1
  <?php
2
  /**
3
- * @package Wsal
 
 
4
  *
 
 
 
 
 
 
 
 
 
 
5
  * Search Add-On promo Page.
6
  * Used only if the plugin is not activated.
 
 
7
  */
8
- class WSAL_Views_Search extends WSAL_AbstractView
9
- {
10
- public function GetTitle()
11
- {
12
- return __('Search Add-On', 'wp-security-audit-log');
13
- }
14
-
15
- public function GetIcon()
16
- {
17
- return 'dashicons-external';
18
- }
19
-
20
- public function GetName()
21
- {
22
- return __('Search', 'wp-security-audit-log');
23
- }
24
-
25
- public function GetWeight()
26
- {
27
- return 9;
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- public function Header()
31
- {
32
- wp_enqueue_style(
33
- 'extensions',
34
- $this->_plugin->GetBaseUrl() . '/css/extensions.css',
35
- array(),
36
- filemtime($this->_plugin->GetBaseDir() . '/css/extensions.css')
37
- );
38
- }
39
-
40
- public function Render()
41
- {
42
- ?>
43
- <div class="wrap-advertising-page-single">
44
- <div class="icon" style='background-image:url("<?=$this->_plugin->GetBaseUrl();?>/img/search.jpg");'></div>
45
- <h3><?php _e('Search', 'wp-security-audit-log'); ?></h3>
46
- <p>
47
- <?php _e('Do free-text based searches for specific activity in the WordPress audit trail.<br> You can also use the built-in filters to fine-tune your searches.', 'wp-security-audit-log'); ?>
48
- </p>
49
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/search-add-on-for-wordpress-security-audit-log/?utm_source=plugin&utm_medium=searchpage&utm_campaign=search'; ?>
50
- <p>
51
- <a class="button-primary" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Learn More', 'wp-security-audit-log'); ?></a>
52
- </p>
53
- <div class="clear"></div>
54
- <p>
55
- <span class="description">
56
- <strong><span class="text-red">70% Off</span> when you purchase this add-on as part of the All Add-On bundle.</strong>
57
- </span>
58
- </p>
59
- <?php $url = 'https://www.wpsecurityauditlog.com/extensions/all-add-ons-60-off/?utm_source=plugin&utm_medium=extensionspage&utm_campaign=alladdons'; ?>
60
- <a class="button-blue" href="<?php echo esc_attr($url); ?>" target="_blank"><?php _e('Buy all Add-Ons Bundle', 'wp-security-audit-log'); ?></a>
61
- </div>
62
- <?php
63
- }
64
  }
1
  <?php
2
  /**
3
+ * View: Users Sessions Page
4
+ *
5
+ * WSAL users sessions page.
6
  *
7
+ * @since 1.0.0
8
+ * @package Wsal
9
+ */
10
+
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
+ /**
17
  * Search Add-On promo Page.
18
  * Used only if the plugin is not activated.
19
+ *
20
+ * @package Wsal
21
  */
22
+ class WSAL_Views_Search extends WSAL_AbstractView {
23
+
24
+ /**
25
+ * Method: Get View Title.
26
+ */
27
+ public function GetTitle() {
28
+ return __( 'Search Add-On', 'wp-security-audit-log' );
29
+ }
30
+
31
+ /**
32
+ * Method: Get View Icon.
33
+ */
34
+ public function GetIcon() {
35
+ return 'dashicons-external';
36
+ }
37
+
38
+ /**
39
+ * Method: Get View Name.
40
+ */
41
+ public function GetName() {
42
+ return __( 'Search &#8682;', 'wp-security-audit-log' );
43
+ }
44
+
45
+ /**
46
+ * Method: Get View Weight.
47
+ */
48
+ public function GetWeight() {
49
+ return 11;
50
+ }
51
+
52
+ /**
53
+ * Method: Get View Header.
54
+ */
55
+ public function Header() {
56
+ // Extension Page CSS.
57
+ wp_enqueue_style(
58
+ 'extensions',
59
+ $this->_plugin->GetBaseUrl() . '/css/extensions.css',
60
+ array(),
61
+ filemtime( $this->_plugin->GetBaseDir() . '/css/extensions.css' )
62
+ );
63
+
64
+ // Swipebox CSS.
65
+ wp_enqueue_style(
66
+ 'wsal-swipebox-css',
67
+ $this->_plugin->GetBaseUrl() . '/css/swipebox.min.css',
68
+ array(),
69
+ filemtime( $this->_plugin->GetBaseDir() . '/css/swipebox.min.css' )
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Method: Get View Footer.
75
+ */
76
+ public function Footer() {
77
+ // jQuery.
78
+ wp_enqueue_script( 'jquery' );
79
+
80
+ // Swipebox JS.
81
+ wp_register_script(
82
+ 'wsal-swipebox-js',
83
+ $this->_plugin->GetBaseUrl() . '/js/jquery.swipebox.min.js',
84
+ array( 'jquery' ),
85
+ filemtime( $this->_plugin->GetBaseDir() . '/js/jquery.swipebox.min.js' )
86
+ );
87
+ wp_enqueue_script( 'wsal-swipebox-js' );
88
+
89
+ // Extensions JS.
90
+ wp_register_script(
91
+ 'wsal-extensions-js',
92
+ $this->_plugin->GetBaseUrl() . '/js/extensions.js',
93
+ array( 'wsal-swipebox-js' ),
94
+ filemtime( $this->_plugin->GetBaseDir() . '/js/extensions.js' )
95
+ );
96
+ wp_enqueue_script( 'wsal-extensions-js' );
97
+ }
98
+
99
+ /**
100
+ * Method: Get View.
101
+ */
102
+ public function Render() {
103
+ ?>
104
+ <div class="wrap-advertising-page-single">
105
+ <div class="wsal-row">
106
+ <div class="wsal-col">
107
+ <div class="icon" style='background-image:url("<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/search.jpg");'></div>
108
+ </div>
109
+ <!-- /.wsal-col -->
110
+
111
+ <div class="wsal-col">
112
+ <h3><?php esc_html_e( 'Search', 'wp-security-audit-log' ); ?></h3>
113
+ <p>
114
+ <?php esc_html_e( 'Upgrade to Premium to:', 'wp-security-audit-log' ); ?>
115
+ </p>
116
+ <p>
117
+ <ul class="wsal-features-list">
118
+ <li><?php esc_html_e( 'Easily find and track back a specific change or suspicious user behaviour,', 'wp-security-audit-log' ); ?></li>
119
+ <li><?php esc_html_e( 'Easily find the root of a problem to ease troubleshooting,', 'wp-security-audit-log' ); ?></li>
120
+ <li><?php esc_html_e( 'Do free-text based searches in the WordPress audit log,', 'wp-security-audit-log' ); ?></li>
121
+ <li><?php esc_html_e( 'Use filters to fine tune the search results,', 'wp-security-audit-log' ); ?></li>
122
+ <li><?php esc_html_e( 'Save search terms & filters for improved productivity,', 'wp-security-audit-log' ); ?></li>
123
+ <li><?php esc_html_e( '& more.', 'wp-security-audit-log' ); ?></li>
124
+ </ul>
125
+ </p>
126
+ <?php
127
+ $buy_now = add_query_arg( 'page', 'wsal-auditlog-pricing', admin_url( 'admin.php' ) );
128
+ $more_info = add_query_arg(
129
+ array(
130
+ 'utm_source' => 'plugin',
131
+ 'utm_medium' => 'page',
132
+ 'utm_content' => 'search+more+info',
133
+ 'utm_campaign' => 'upgrade+premium',
134
+ ),
135
+ 'https://www.wpsecurityauditlog.com/premium-features/'
136
+ );
137
+ ?>
138
+ <p>
139
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $buy_now ); ?>"><?php esc_html_e( 'Upgrade to Premium', 'wp-security-audit-log' ); ?></a>
140
+ <a class="button-primary wsal-extension-btn" href="<?php echo esc_attr( $more_info ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'wp-security-audit-log' ); ?></a>
141
+ </p>
142
+ </div>
143
+ <!-- /.wsal-col -->
144
+ </div>
145
+ <!-- /.wsal-row -->
146
+
147
+ <div class="wsal-row">
148
+ <div class="wsal-col">
149
+ <h3><?php esc_html_e( 'Screenshots', 'wp-security-audit-log' ); ?></h3>
150
 
151
+ <p>
152
+ <ul class="wsal-features-list">
153
+ <li>
154
+ <?php esc_html_e( 'Use the free-text based search to find a specific change and use the filters to fine tune the search results.', 'wp-security-audit-log' ); ?><br />
155
+ <a class="swipebox" title="<?php esc_attr_e( 'Use the free-text based search to find a specific change and use the filters to fine tune the search results.', 'wp-security-audit-log' ); ?>"
156
+ href="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/search/search.png">
157
+ <img width="500" src="<?php echo esc_url( $this->_plugin->GetBaseUrl() ); ?>/img/search/search.png">
158
+ </a>
159
+ </li>
160
+ </ul>
161
+ </p>
162
+ </div>
163
+ </div>
164
+ <!-- /.wsal-row -->
165
+ </div>
166
+ <?php
167
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
classes/Views/Settings.php CHANGED
@@ -8,6 +8,11 @@
8
  * @package Wsal
9
  */
10
 
 
 
 
 
 
11
  /**
12
  * Class: WSAL_Views_Settings
13
  *
@@ -17,8 +22,18 @@
17
  */
18
  class WSAL_Views_Settings extends WSAL_AbstractView {
19
 
20
- public $adapterMsg = '';
 
 
 
 
 
21
 
 
 
 
 
 
22
  public function __construct( WpSecurityAuditLog $plugin ) {
23
  parent::__construct( $plugin );
24
  add_action( 'wp_ajax_AjaxCheckSecurityToken', array( $this, 'AjaxCheckSecurityToken' ) );
@@ -28,33 +43,47 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
28
  add_action( 'wp_ajax_AjaxGetAllCPT', array( $this, 'AjaxGetAllCPT' ) );
29
  }
30
 
31
- public function HasPluginShortcutLink()
32
- {
 
 
33
  return true;
34
  }
35
 
36
- public function GetTitle()
37
- {
38
- return __('Settings', 'wp-security-audit-log');
 
 
39
  }
40
 
41
- public function GetIcon()
42
- {
 
 
43
  return 'dashicons-admin-generic';
44
  }
45
 
46
- public function GetName()
47
- {
48
- return __('Settings', 'wp-security-audit-log');
 
 
49
  }
50
 
51
- public function GetWeight()
52
- {
 
 
53
  return 3;
54
  }
55
 
 
 
 
 
 
56
  protected function GetTokenType( $token ) {
57
-
58
  // Get users.
59
  $users = array();
60
  foreach ( get_users( 'blog_id=0&fields[]=user_login' ) as $obj ) {
@@ -67,12 +96,17 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
67
  // Get custom post types.
68
  $post_types = get_post_types( array(), 'names', 'and' );
69
 
 
70
  if ( in_array( $token, $users ) ) {
71
  return 'user';
72
  }
 
 
73
  if ( in_array( $token, $roles ) ) {
74
  return 'role';
75
  }
 
 
76
  if ( in_array( $token, $post_types ) ) {
77
  return 'cpts';
78
  }
@@ -81,97 +115,125 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
81
 
82
  /**
83
  * Method: Save settings.
84
- *
85
- * @since 1.0.0
86
  */
87
  protected function Save() {
88
  check_admin_referer( 'wsal-settings' );
89
- $this->_plugin->settings->SetPruningDateEnabled( isset( $_REQUEST['PruneBy'] ) ? $_REQUEST['PruneBy'] == 'date' : '' );
90
- $this->_plugin->settings->SetPruningDate( isset( $_REQUEST['PruningDate'] ) ? $_REQUEST['PruningDate'] : '' );
91
- $this->_plugin->settings->SetPruningLimitEnabled( isset( $_REQUEST['PruneBy'] ) ? $_REQUEST['PruneBy'] == 'limit' : '' );
92
- $this->_plugin->settings->SetPruningLimit( isset( $_REQUEST['PruningLimit'] ) ? $_REQUEST['PruningLimit'] : '' );
93
-
94
- $this->_plugin->settings->SetFromEmail( $_REQUEST['FromEmail'] );
95
- $this->_plugin->settings->SetDisplayName( $_REQUEST['DisplayName'] );
96
-
97
- $this->_plugin->settings->SetWidgetsEnabled( $_REQUEST['EnableDashboardWidgets'] );
98
- $this->_plugin->settings->SetAllowedPluginViewers( isset( $_REQUEST['Viewers'] ) ? $_REQUEST['Viewers'] : array() );
99
- $this->_plugin->settings->SetAllowedPluginEditors( isset( $_REQUEST['Editors'] ) ? $_REQUEST['Editors'] : array() );
100
-
101
- $this->_plugin->settings->SetExcludedMonitoringUsers( isset( $_REQUEST['ExUsers'] ) ? $_REQUEST['ExUsers'] : array() );
102
- $this->_plugin->settings->SetExcludedMonitoringRoles( isset( $_REQUEST['ExRoles'] ) ? $_REQUEST['ExRoles'] : array() );
103
- $this->_plugin->settings->SetExcludedMonitoringCustom( isset( $_REQUEST['Customs'] ) ? $_REQUEST['Customs'] : array() );
104
- $this->_plugin->settings->SetExcludedMonitoringIP( isset( $_REQUEST['IpAddrs'] ) ? $_REQUEST['IpAddrs'] : array() );
105
- $this->_plugin->settings->set_excluded_post_types( isset( $_REQUEST['ExCPTss'] ) ? $_REQUEST['ExCPTss'] : array() );
106
-
107
- $this->_plugin->settings->SetRestrictAdmins( isset( $_REQUEST['RestrictAdmins'] ) );
108
- $this->_plugin->settings->SetRefreshAlertsEnabled( $_REQUEST['EnableAuditViewRefresh'] );
109
- $this->_plugin->settings->SetMainIPFromProxy( isset( $_REQUEST['EnableProxyIpCapture'] ) );
110
- $this->_plugin->settings->SetInternalIPsFiltering( isset( $_REQUEST['EnableIpFiltering'] ) );
111
- $this->_plugin->settings->SetIncognito( isset( $_REQUEST['Incognito'] ) );
112
- $this->_plugin->settings->SetLoggingDisabled( isset( $_REQUEST['Logging'] ) );
113
- $this->_plugin->settings->SetDeleteData( isset( $_REQUEST['DeleteData'] ) );
114
- $this->_plugin->settings->SetTimezone( $_REQUEST['Timezone'] );
115
- $this->_plugin->settings->set_type_username( $_REQUEST['type_username'] );
116
- $this->_plugin->settings->SetWPBackend( isset( $_REQUEST['WPBackend'] ) );
117
- if ( ! empty( $_REQUEST['Columns'] ) ) {
118
- $this->_plugin->settings->SetColumns( $_REQUEST['Columns'] );
 
 
 
 
 
 
 
 
119
  }
120
  $this->_plugin->settings->ClearDevOptions();
121
 
122
- if ( isset( $_REQUEST['DevOptions'] ) ) {
123
- foreach ( $_REQUEST['DevOptions'] as $opt ) {
124
  $this->_plugin->settings->SetDevOptionEnabled( $opt, true );
125
  }
126
  }
127
  }
128
 
 
 
 
129
  public function AjaxCheckSecurityToken() {
130
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
131
  die( 'Access Denied.' );
132
  }
133
- if ( ! isset( $_REQUEST['token'] ) ) {
 
 
 
 
134
  die( 'Token parameter expected.' );
135
  }
136
- die( $this->GetTokenType( $_REQUEST['token'] ) );
137
  }
138
 
139
- public function AjaxRunCleanup()
140
- {
141
- if (!$this->_plugin->settings->CurrentUserCan('view')) {
142
- die('Access Denied.');
 
 
143
  }
144
  $this->_plugin->CleanUp();
145
- wp_redirect($this->GetUrl());
146
  exit;
147
  }
148
 
149
- public function Render()
150
- {
151
- if (!$this->_plugin->settings->CurrentUserCan('edit')) {
152
- wp_die(__('You do not have sufficient permissions to access this page.', 'wp-security-audit-log'));
 
 
 
 
 
153
  }
154
- if (isset($_POST['submit'])) {
 
 
 
 
 
155
  try {
156
  $this->Save();
157
  ?><div class="updated">
158
- <p><?php _e('Settings have been saved.', 'wp-security-audit-log'); ?></p>
159
- </div><?php
160
- } catch (Exception $ex) {
161
- ?><div class="error"><p><?php _e('Error: ', 'wp-security-audit-log'); ?><?php echo $ex->getMessage(); ?></p></div><?php
 
 
 
162
  }
163
  }
164
  ?>
165
  <h2 id="wsal-tabs" class="nav-tab-wrapper">
166
- <a href="#tab-general" class="nav-tab"><?php _e('General', 'wp-security-audit-log'); ?></a>
167
- <a href="#tab-audit-log" class="nav-tab"><?php _e('Audit Log', 'wp-security-audit-log'); ?></a>
168
- <a href="#tab-exclude" class="nav-tab"><?php _e('Exclude Objects', 'wp-security-audit-log'); ?></a>
169
  </h2>
170
- <script src="//code.jquery.com/ui/1.10.3/jquery-ui.js"/></script>
171
  <form id="audit-log-settings" method="post">
172
- <input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
173
- <input type="hidden" id="ajaxurl" value="<?php echo esc_attr(admin_url('admin-ajax.php')); ?>" />
174
- <?php wp_nonce_field('wsal-settings'); ?>
175
 
176
  <div id="audit-log-adverts">
177
  </div>
@@ -181,159 +243,168 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
181
  <tbody>
182
  <!-- From Email & Name -->
183
  <tr>
184
- <th><label for="FromEmail"><?php _e('From Email & Name', 'wp-security-audit-log'); ?></label></th>
185
  <td>
186
  <fieldset>
187
- <label for="FromEmail"><?php _e('Email Address', 'wp-security-audit-log'); ?></label>
188
- <input type="email" id="FromEmail" name="FromEmail" value="<?php echo esc_attr($this->_plugin->settings->GetFromEmail()); ?>" />
189
  &nbsp;
190
- <label for="DisplayName"><?php _e('Display Name', 'wp-security-audit-log'); ?></label>
191
- <input type="text" id="DisplayName" name="DisplayName" value="<?php echo esc_attr($this->_plugin->settings->GetDisplayName()); ?>" />
192
  </fieldset>
193
  <p class="description">
194
  <?php
195
- echo sprintf(
196
- __('These email address and display name will be used as From details in the emails sent by the %s . Please ensure the mail server can relay emails with the domain of the specified email address.', 'wp-security-audit-log'),
197
- '<a target="_blank" href="https://www.wpsecurityauditlog.com/plugin-extensions/">' . __('(premium add-ons)', 'wp-security-audit-log') . '</a>'
198
- );
199
  ?>
200
  </p>
201
  </td>
202
  </tr>
203
  <!-- Alerts Dashboard Widget -->
204
  <tr>
205
- <th><label for="dwoption_on"><?php _e('Alerts Dashboard Widget', 'wp-security-audit-log'); ?></label></th>
206
  <td>
207
  <fieldset>
208
  <?php $dwe = $this->_plugin->settings->IsWidgetsEnabled(); ?>
209
  <label for="dwoption_on">
210
- <input type="radio" name="EnableDashboardWidgets" id="dwoption_on" style="margin-top: 2px;" <?php if($dwe)echo 'checked="checked"'; ?> value="1">
211
- <span><?php _e('On', 'wp-security-audit-log'); ?></span>
212
  </label>
213
  <br/>
214
  <label for="dwoption_off">
215
- <input type="radio" name="EnableDashboardWidgets" id="dwoption_off" style="margin-top: 2px;" <?php if(!$dwe)echo 'checked="checked"'; ?> value="0">
216
- <span><?php _e('Off', 'wp-security-audit-log'); ?></span>
217
  </label>
218
  <br/>
219
- <p class="description"><?php
 
220
  echo sprintf(
221
- __('Display a dashboard widget with the latest %d security alerts.', 'wp-security-audit-log'),
222
- $this->_plugin->settings->GetDashboardWidgetMaxAlerts()
223
- );
224
- ?></p>
 
225
  </fieldset>
226
  </td>
227
  </tr>
228
  <!-- Reverse Proxy / Firewall Options -->
229
  <tr>
230
- <th><label for="pioption_on"><?php _e('Reverse Proxy / Firewall Options', 'wp-security-audit-log'); ?></label></th>
231
  <td>
232
  <fieldset>
233
  <label for="EnableProxyIpCapture">
234
- <input type="checkbox" name="EnableProxyIpCapture" value="1" id="EnableProxyIpCapture"<?php
235
- if($this->_plugin->settings->IsMainIPFromProxy())echo ' checked="checked"';
236
- ?>/> <?php _e('WordPress running behind firewall or proxy', 'wp-security-audit-log'); ?>
237
  </label>
238
  <br/>
239
- <span class="description"><?php _e('Enable this option if your WordPress is running behind a firewall or reverse proxy. When this option is enabled the plugin will retrieve the user\'s IP address from the proxy header.', 'wp-security-audit-log'); ?></span>
240
  <br/>
241
  <label for="EnableIpFiltering">
242
- <input type="checkbox" name="EnableIpFiltering" value="1" id="EnableIpFiltering"<?php
243
- if($this->_plugin->settings->IsInternalIPsFiltered())echo ' checked="checked"';
244
- ?>/> <?php _e('Filter Internal IP Addresses', 'wp-security-audit-log'); ?>
245
  </label>
246
  <br/>
247
- <span class="description"><?php _e('Enable this option to filter internal IP addresses from the proxy headers.', 'wp-security-audit-log'); ?></span>
248
  </fieldset>
249
  </td>
250
  </tr>
251
  <!-- Can Manage Plugin -->
252
  <tr>
253
- <th><label for="EditorQueryBox"><?php _e('Can Manage Plugin', 'wp-security-audit-log'); ?></label></th>
254
  <td>
255
  <fieldset>
256
  <input type="text" id="EditorQueryBox" style="float: left; display: block; width: 250px;">
257
  <input type="button" id="EditorQueryAdd" style="float: left; display: block;" class="button-primary" value="Add">
258
  <br style="clear: both;"/>
259
- <p class="description"><?php
260
- _e('Users and Roles in this list can manage the plugin settings', 'wp-security-audit-log');
261
- ?></p>
262
- <div id="EditorList"><?php
263
- foreach ($this->_plugin->settings->GetAllowedPluginEditors() as $item) {
264
- ?><span class="sectoken-<?php echo $this->GetTokenType($item); ?>">
265
- <input type="hidden" name="Editors[]" value="<?php echo esc_attr($item); ?>"/>
266
- <?php echo esc_html($item); ?>
267
- <?php if (wp_get_current_user()->user_login != $item) { ?>
268
  <a href="javascript:;" title="Remove">&times;</a>
269
  <?php } ?>
270
- </span><?php
271
- }
272
- ?></div>
273
  </fieldset>
274
  </td>
275
  </tr>
276
  <!-- Restrict Plugin Access -->
277
  <tr>
278
- <th><label for="RestrictAdmins"><?php _e('Restrict Plugin Access', 'wp-security-audit-log'); ?></label></th>
279
  <td>
280
  <fieldset>
281
- <input type="hidden" id="RestrictAdminsDefaultUser" value="<?php echo esc_attr(wp_get_current_user()->user_login); ?>"/>
282
  <label for="RestrictAdmins">
283
  <?php $ira = $this->_plugin->settings->IsRestrictAdmins(); ?>
284
- <input type="checkbox" name="RestrictAdmins" id="RestrictAdmins"<?php if($ira)echo ' checked="checked"'; ?>/>
285
  </label>
286
  <br/>
287
  <span class="description">
288
- <?php _e('If this option is disabled all the administrators on this WordPress have access to manage this plugin.', 'wp-security-audit-log'); ?><br/>
289
- <?php _e('By enabling this option only <strong>You</strong> and the users specified in the <strong>Can Manage Plugin</strong> and <strong>Can View Alerts</strong> can configure this plugin or view the alerts in the WordPress audit trail.', 'wp-security-audit-log'); ?>
290
  </span>
291
  </fieldset>
292
  </td>
293
  </tr>
294
  <!-- Developer Options -->
295
  <tr>
296
- <th><label><?php _e('Developer Options', 'wp-security-audit-log'); ?></label></th>
297
  <td>
298
  <fieldset>
299
  <?php $any = $this->_plugin->settings->IsAnyDevOptionEnabled(); ?>
300
- <a href="javascript:;" style="<?php if($any)echo 'display: none;'; ?>"
301
- onclick="jQuery(this).hide().next().show();">Show Developer Options</a>
302
- <div style="<?php if(!$any)echo 'display: none;'; ?>">
 
 
303
  <p style="border-left: 3px solid #FFD000; padding: 2px 8px; margin-left: 6px; margin-bottom: 16px;">
304
- <?php _e('Only enable these options on testing, staging and development websites. Enabling any of the settings below on LIVE websites may cause unintended side-effects including degraded performance.', 'wp-security-audit-log'); ?>
305
  </p>
306
  <?php
307
- foreach (array(
308
- WSAL_Settings::OPT_DEV_DATA_INSPECTOR => array(
309
- __('Data Inspector', 'wp-security-audit-log'),
310
- __('View data logged for each triggered alert.', 'wp-security-audit-log')
311
- ),
312
- /* WSAL_Settings::OPT_DEV_PHP_ERRORS => array(
313
- __('PHP Errors', 'wp-security-audit-log'),
314
- __('Enables sensor for alerts generated from PHP.', 'wp-security-audit-log')
315
- ), */
316
- WSAL_Settings::OPT_DEV_REQUEST_LOG => array(
317
- __('Request Log', 'wp-security-audit-log'),
318
- __('Enables logging request to file.', 'wp-security-audit-log')
319
- ),
320
- /* WSAL_Settings::OPT_DEV_BACKTRACE_LOG => array(
321
- __('Backtrace', 'wp-security-audit-log'),
322
- __('Log full backtrace for PHP-generated alerts.', 'wp-security-audit-log')
323
- ), */
324
- ) as $opt => $info) {
325
- ?><label for="devoption_<?php echo $opt; ?>">
326
- <input type="checkbox" name="DevOptions[]" id="devoption_<?php echo $opt; ?>" <?php
327
- if($this->_plugin->settings->IsDevOptionEnabled($opt))echo 'checked="checked"'; ?> value="<?php echo $opt; ?>">
328
- <span><?php echo $info[0]; ?></span>
329
- <?php if (isset($info[1]) && $info[1]) { ?>
330
- <span class="description"> &mdash; <?php echo $info[1]; ?></span>
331
- <?php }
332
- ?></label><br/><?php
333
- }
 
 
 
 
 
 
 
334
  ?>
335
  <span class="description">
336
- <?php _e('The request log file is saved in the /wp-content/uploads/wp-security-audit-log/ directory.', 'wp-security-audit-log'); ?>
337
  </span>
338
  </div>
339
  </fieldset>
@@ -341,24 +412,23 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
341
  </tr>
342
  <!-- Hide Plugin in Plugins Page -->
343
  <tr>
344
- <th><label for="Incognito"><?php _e('Hide Plugin in Plugins Page', 'wp-security-audit-log'); ?></label></th>
345
  <td>
346
  <fieldset>
347
  <label for="Incognito">
348
- <input type="checkbox" name="Incognito" value="1" id="Incognito"<?php
349
- if ($this->_plugin->settings->IsIncognito()) echo ' checked="checked"';
350
- ?>/> <?php _e('Hide', 'wp-security-audit-log'); ?>
351
  </label>
352
  <br/>
353
  <span class="description">
354
- <?php _e('To manually revert this setting set the value of option wsal-hide-plugin to 0 in the wp_wsal_options table.', 'wp-security-audit-log'); ?>
355
  </span>
356
  </fieldset>
357
  </td>
358
  </tr>
359
  <!-- Logging -->
360
  <tr>
361
- <th><label for="Logging"><?php _e('Logging', 'wp-security-audit-log'); ?></label></th>
362
  <td>
363
  <fieldset>
364
  <label for="Logging">
@@ -372,20 +442,22 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
372
  </label>
373
  <br/>
374
  <span class="description">
375
- <?php _e('Disable all plugin logging.', 'wp-security-audit-log'); ?>
376
  </span>
377
  </fieldset>
378
  </td>
379
  </tr>
380
  <!-- Remove Data on Uninstall -->
381
  <tr>
382
- <th><label for="DeleteData"><?php _e('Remove Data on Uninstall', 'wp-security-audit-log'); ?></label></th>
383
  <td>
384
  <fieldset>
385
  <label for="DeleteData">
386
- <input type="checkbox" name="DeleteData" value="1" id="DeleteData" onclick="return delete_confirm(this);"<?php
387
- if ($this->_plugin->settings->IsDeleteData()) echo ' checked="checked"';
388
- ?>/> <span class="description">Check this box if you would like remove all data when the plugin is deleted.</span>
 
 
389
  </label>
390
  </fieldset>
391
  </td>
@@ -397,124 +469,142 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
397
  <tbody>
398
  <!-- Security Alerts Pruning -->
399
  <?php
400
- $disabled = '';
401
- if ($this->_plugin->settings->IsArchivingEnabled()) {
402
  $disabled = 'disabled';
403
  ?>
404
  <tr>
405
  <td colspan="2">
406
- <?php _e('The options below are disabled because you enabled archiving of alerts to the archiving table from', 'wp-security-audit-log'); ?>&nbsp;<a href="<?php echo admin_url('admin.php?page=wsal-ext-settings#mirroring'); ?>" target="_blank">here</a>
407
  </td>
408
  </tr>
409
  <?php } ?>
410
  <tr>
411
- <th><label for="delete1"><?php _e('Security Alerts Pruning', 'wp-security-audit-log'); ?></label></th>
412
  <td>
413
  <fieldset>
414
- <?php $text = __('(eg: 1 month)', 'wp-security-audit-log'); ?>
415
- <?php $nbld = !($this->_plugin->settings->IsPruningDateEnabled() || $this->_plugin->settings->IsPruningLimitEnabled()); ?>
416
  <label for="delete0">
417
- <input type="radio" id="delete0" name="PruneBy" value="" <?php if ($nbld) echo 'checked="checked"'; ?> <?php echo $disabled; ?>/>
418
- <?php echo __('None', 'wp-security-audit-log'); ?>
8
  * @package Wsal
9
  */
10
 
11
+ // Exit if accessed directly.
12
+ if ( ! defined( 'ABSPATH' ) ) {
13
+ exit;
14
+ }
15
+
16
  /**
17
  * Class: WSAL_Views_Settings
18
  *
22
  */
23
  class WSAL_Views_Settings extends WSAL_AbstractView {
24
 
25
+ /**
26
+ * Adapter Message.
27
+ *
28
+ * @var string
29
+ */
30
+ public $adapter_msg = '';
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
  add_action( 'wp_ajax_AjaxCheckSecurityToken', array( $this, 'AjaxCheckSecurityToken' ) );
43
  add_action( 'wp_ajax_AjaxGetAllCPT', array( $this, 'AjaxGetAllCPT' ) );
44
  }
45
 
46
+ /**
47
+ * Method: Plugin Shortcut.
48
+ */
49
+ public function HasPluginShortcutLink() {
50
  return true;
51
  }
52
 
53
+ /**
54
+ * Method: Get View Title.
55
+ */
56
+ public function GetTitle() {
57
+ return __( 'Settings', 'wp-security-audit-log' );
58
  }
59
 
60
+ /**
61
+ * Method: Get View Icon.
62
+ */
63
+ public function GetIcon() {
64
  return 'dashicons-admin-generic';
65
  }
66
 
67
+ /**
68
+ * Method: Get View Name.
69
+ */
70
+ public function GetName() {
71
+ return __( 'Settings', 'wp-security-audit-log' );
72
  }
73
 
74
+ /**
75
+ * Method: Get View Weight.
76
+ */
77
+ public function GetWeight() {
78
  return 3;
79
  }
80
 
81
+ /**
82
+ * Method: Get Token Type.
83
+ *
84
+ * @param string $token - Token type.
85
+ */
86
  protected function GetTokenType( $token ) {
 
87
  // Get users.
88
  $users = array();
89
  foreach ( get_users( 'blog_id=0&fields[]=user_login' ) as $obj ) {
96
  // Get custom post types.
97
  $post_types = get_post_types( array(), 'names', 'and' );
98
 
99
+ // Check if the token matched users.
100
  if ( in_array( $token, $users ) ) {
101
  return 'user';
102
  }
103
+
104
+ // Check if the token matched user roles.
105
  if ( in_array( $token, $roles ) ) {
106
  return 'role';
107
  }
108
+
109
+ // Check if the token matched post types.
110
  if ( in_array( $token, $post_types ) ) {
111
  return 'cpts';
112
  }
115
 
116
  /**
117
  * Method: Save settings.
 
 
118
  */
119
  protected function Save() {
120
  check_admin_referer( 'wsal-settings' );
121
+
122
+ // Filter $_POST array for security.
123
+ $post_array = filter_input_array( INPUT_POST );
124
+
125
+ // Get pruning date.
126
+ $pruning_date = isset( $post_array['PruningDate'] ) ? (int) $post_array['PruningDate'] : '';
127
+ $pruning_date = ( ! empty( $pruning_date ) ) ? $pruning_date . ' months' : '';
128
+
129
+ $this->_plugin->settings->SetPruningDateEnabled( isset( $post_array['PruneBy'] ) ? 'date' === $post_array['PruneBy'] : '' );
130
+ $this->_plugin->settings->SetPruningDate( $pruning_date );
131
+ $this->_plugin->settings->SetPruningLimitEnabled( isset( $post_array['PruneBy'] ) ? 'limit' === $post_array['PruneBy'] : '' );
132
+ $this->_plugin->settings->SetPruningLimit( isset( $post_array['PruningLimit'] ) ? $post_array['PruningLimit'] : '' );
133
+
134
+ $this->_plugin->settings->SetFromEmail( $post_array['FromEmail'] );
135
+ $this->_plugin->settings->SetDisplayName( $post_array['DisplayName'] );
136
+
137
+ $this->_plugin->settings->SetWidgetsEnabled( $post_array['EnableDashboardWidgets'] );
138
+ $this->_plugin->settings->SetAllowedPluginViewers( isset( $post_array['Viewers'] ) ? $post_array['Viewers'] : array() );
139
+ $this->_plugin->settings->SetAllowedPluginEditors( isset( $post_array['Editors'] ) ? $post_array['Editors'] : array() );
140
+
141
+ $this->_plugin->settings->SetExcludedMonitoringUsers( isset( $post_array['ExUsers'] ) ? $post_array['ExUsers'] : array() );
142
+ $this->_plugin->settings->SetExcludedMonitoringRoles( isset( $post_array['ExRoles'] ) ? $post_array['ExRoles'] : array() );
143
+ $this->_plugin->settings->SetExcludedMonitoringCustom( isset( $post_array['Customs'] ) ? $post_array['Customs'] : array() );
144
+ $this->_plugin->settings->SetExcludedMonitoringIP( isset( $post_array['IpAddrs'] ) ? $post_array['IpAddrs'] : array() );
145
+ $this->_plugin->settings->set_excluded_post_types( isset( $post_array['ExCPTss'] ) ? $post_array['ExCPTss'] : array() );
146
+
147
+ $this->_plugin->settings->SetRestrictAdmins( isset( $post_array['RestrictAdmins'] ) );
148
+ $this->_plugin->settings->SetRefreshAlertsEnabled( $post_array['EnableAuditViewRefresh'] );
149
+ $this->_plugin->settings->SetMainIPFromProxy( isset( $post_array['EnableProxyIpCapture'] ) );
150
+ $this->_plugin->settings->SetInternalIPsFiltering( isset( $post_array['EnableIpFiltering'] ) );
151
+ $this->_plugin->settings->SetIncognito( isset( $post_array['Incognito'] ) );
152
+ $this->_plugin->settings->SetLoggingDisabled( isset( $post_array['Logging'] ) );
153
+ $this->_plugin->settings->SetDeleteData( isset( $post_array['DeleteData'] ) );
154
+ $this->_plugin->settings->SetTimezone( $post_array['Timezone'] );
155
+ $this->_plugin->settings->set_type_username( $post_array['type_username'] );
156
+ $this->_plugin->settings->SetWPBackend( isset( $post_array['WPBackend'] ) );
157
+ if ( ! empty( $post_array['Columns'] ) ) {
158
+ $this->_plugin->settings->SetColumns( $post_array['Columns'] );
159
  }
160
  $this->_plugin->settings->ClearDevOptions();
161
 
162
+ if ( isset( $post_array['DevOptions'] ) ) {
163
+ foreach ( $post_array['DevOptions'] as $opt ) {
164
  $this->_plugin->settings->SetDevOptionEnabled( $opt, true );
165
  }
166
  }
167
  }
168
 
169
+ /**
170
+ * Method: Check security token.
171
+ */
172
  public function AjaxCheckSecurityToken() {
173
  if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
174
  die( 'Access Denied.' );
175
  }
176
+
177
+ // Filter $_POST array for security.
178
+ $post_array = filter_input_array( INPUT_POST );
179
+
180
+ if ( ! isset( $post_array['token'] ) ) {
181
  die( 'Token parameter expected.' );
182
  }
183
+ die( esc_html( $this->GetTokenType( $post_array['token'] ) ) );
184
  }
185
 
186
+ /**
187
+ * Method: Run cleanup.
188
+ */
189
+ public function AjaxRunCleanup() {
190
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'view' ) ) {
191
+ die( 'Access Denied.' );
192
  }
193
  $this->_plugin->CleanUp();
194
+ wp_safe_redirect( $this->GetUrl() );
195
  exit;
196
  }
197
 
198
+ /**
199
+ * Method: Get View.
200
+ */
201
+ public function Render() {
202
+ // Filter $_POST array for security.
203
+ $post_array = filter_input_array( INPUT_POST );
204
+
205
+ if ( isset( $post_array['_wpnonce'] ) && ! wp_verify_nonce( $post_array['_wpnonce'], 'wsal-settings' ) ) {
206
+ wp_die( esc_html__( 'Nonce verification failed.', 'wp-security-audit-log' ) );
207
  }
208
+
209
+ if ( ! $this->_plugin->settings->CurrentUserCan( 'edit' ) ) {
210
+ wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wp-security-audit-log' ) );
211
+ }
212
+
213
+ if ( isset( $post_array['submit'] ) ) {
214
  try {
215
  $this->Save();
216
  ?><div class="updated">
217
+ <p><?php esc_html_e( 'Settings have been saved.', 'wp-security-audit-log' ); ?></p>
218
+ </div>
219
+ <?php
220
+ } catch ( Exception $ex ) {
221
+ ?>
222
+ <div class="error"><p><?php esc_html_e( 'Error: ', 'wp-security-audit-log' ); ?><?php echo esc_html( $ex->getMessage() ); ?></p></div>
223
+ <?php
224
  }
225
  }
226
  ?>
227
  <h2 id="wsal-tabs" class="nav-tab-wrapper">
228
+ <a href="#tab-general" class="nav-tab"><?php esc_html_e( 'General', 'wp-security-audit-log' ); ?></a>
229
+ <a href="#tab-audit-log" class="nav-tab"><?php esc_html_e( 'Audit Log', 'wp-security-audit-log' ); ?></a>
230
+ <a href="#tab-exclude" class="nav-tab"><?php esc_html_e( 'Exclude Objects', 'wp-security-audit-log' ); ?></a>
231
  </h2>
232
+
233
  <form id="audit-log-settings" method="post">
234
+ <input type="hidden" name="page" value="<?php echo filter_input( INPUT_GET, 'page', FILTER_SANITIZE_STRING ); ?>" />
235
+ <input type="hidden" id="ajaxurl" value="<?php echo esc_attr( admin_url( 'admin-ajax.php' ) ); ?>" />
236
+ <?php wp_nonce_field( 'wsal-settings' ); ?>
237
 
238
  <div id="audit-log-adverts">
239
  </div>
243
  <tbody>
244
  <!-- From Email & Name -->
245
  <tr>
246
+ <th><label for="FromEmail"><?php esc_html_e( 'From Email & Name', 'wp-security-audit-log' ); ?></label></th>
247
  <td>
248
  <fieldset>
249
+ <label for="FromEmail"><?php esc_html_e( 'Email Address', 'wp-security-audit-log' ); ?></label>
250
+ <input type="email" id="FromEmail" name="FromEmail" value="<?php echo esc_attr( $this->_plugin->settings->GetFromEmail() ); ?>" />
251
  &nbsp;
252
+ <label for="DisplayName"><?php esc_html_e( 'Display Name', 'wp-security-audit-log' ); ?></label>
253
+ <input type="text" id="DisplayName" name="DisplayName" value="<?php echo esc_attr( $this->_plugin->settings->GetDisplayName() ); ?>" />
254
  </fieldset>
255
  <p class="description">
256
  <?php
257
+ echo sprintf(
258
+ esc_html__( 'These email address and display name will be used as From details in the emails sent by the %s . Please ensure the mail server can relay emails with the domain of the specified email address.', 'wp-security-audit-log' ),
259
+ '<a target="_blank" href="https://www.wpsecurityauditlog.com/plugin-extensions/">' . esc_html__( '(premium add-ons)', 'wp-security-audit-log' ) . '</a>'
260
+ );
261
  ?>
262
  </p>
263
  </td>
264
  </tr>
265
  <!-- Alerts Dashboard Widget -->
266
  <tr>
267
+ <th><label for="dwoption_on"><?php esc_html_e( 'Alerts Dashboard Widget', 'wp-security-audit-log' ); ?></label></th>
268
  <td>
269
  <fieldset>
270
  <?php $dwe = $this->_plugin->settings->IsWidgetsEnabled(); ?>
271
  <label for="dwoption_on">
272
+ <input type="radio" name="EnableDashboardWidgets" id="dwoption_on" style="margin-top: 2px;" <?php checked( $dwe ); ?> value="1">
273
+ <span><?php esc_html_e( 'On', 'wp-security-audit-log' ); ?></span>
274
  </label>
275
  <br/>
276
  <label for="dwoption_off">
277
+ <input type="radio" name="EnableDashboardWidgets" id="dwoption_off" style="margin-top: 2px;" <?php checked( $dwe, false ); ?> value="0">
278
+ <span><?php esc_html_e( 'Off', 'wp-security-audit-log' ); ?></span>
279
  </label>
280
  <br/>
281
+ <p class="description">
282
+ <?php
283
  echo sprintf(
284
+ esc_html__( 'Display a dashboard widget with the latest %d security alerts.', 'wp-security-audit-log' ),
285
+ esc_html( $this->_plugin->settings->GetDashboardWidgetMaxAlerts() )
286
+ );
287
+ ?>
288
+ </p>
289
  </fieldset>
290
  </td>
291
  </tr>
292
  <!-- Reverse Proxy / Firewall Options -->
293
  <tr>
294
+ <th><label for="pioption_on"><?php esc_html_e( 'Reverse Proxy / Firewall Options', 'wp-security-audit-log' ); ?></label></th>
295
  <td>
296
  <fieldset>
297
  <label for="EnableProxyIpCapture">
298
+ <input type="checkbox" name="EnableProxyIpCapture" value="1" id="EnableProxyIpCapture" <?php checked( $this->_plugin->settings->IsMainIPFromProxy() ); ?> />
299
+ <?php esc_html_e( 'WordPress running behind firewall or proxy', 'wp-security-audit-log' ); ?>
 
300
  </label>
301
  <br/>
302
+ <span class="description"><?php esc_html_e( 'Enable this option if your WordPress is running behind a firewall or reverse proxy. When this option is enabled the plugin will retrieve the user\'s IP address from the proxy header.', 'wp-security-audit-log' ); ?></span>
303
  <br/>
304
  <label for="EnableIpFiltering">
305
+ <input type="checkbox" name="EnableIpFiltering" value="1" id="EnableIpFiltering" <?php checked( $this->_plugin->settings->IsInternalIPsFiltered() ); ?> />
306
+ <?php esc_html_e( 'Filter Internal IP Addresses', 'wp-security-audit-log' ); ?>
 
307
  </label>
308
  <br/>
309
+ <span class="description"><?php esc_html_e( 'Enable this option to filter internal IP addresses from the proxy headers.', 'wp-security-audit-log' ); ?></span>
310
  </fieldset>
311
  </td>
312
  </tr>
313
  <!-- Can Manage Plugin -->
314
  <tr>
315
+ <th><label for="EditorQueryBox"><?php esc_html_e( 'Can Manage Plugin', 'wp-security-audit-log' ); ?></label></th>
316
  <td>
317
  <fieldset>
318
  <input type="text" id="EditorQueryBox" style="float: left; display: block; width: 250px;">
319
  <input type="button" id="EditorQueryAdd" style="float: left; display: block;" class="button-primary" value="Add">
320
  <br style="clear: both;"/>
321
+ <p class="description">
322
+ <?php esc_html_e( 'Users and Roles in this list can manage the plugin settings', 'wp-security-audit-log' ); ?>
323
+ </p>
324
+ <div id="EditorList">
325
+ <?php foreach ( $this->_plugin->settings->GetAllowedPluginEditors() as $item ) : ?>
326
+ <span class="sectoken-<?php echo esc_attr( $this->GetTokenType( $item ) ); ?>">
327
+ <input type="hidden" name="Editors[]" value="<?php echo esc_attr( $item ); ?>"/>
328
+ <?php echo esc_html( $item ); ?>
329
+ <?php if ( wp_get_current_user()->user_login !== $item ) { ?>
330
  <a href="javascript:;" title="Remove">&times;</a>
331
  <?php } ?>
332
+ </span>
333
+ <?php endforeach; ?>
334
+ </div>
335
  </fieldset>
336
  </td>
337
  </tr>
338
  <!-- Restrict Plugin Access -->
339
  <tr>
340
+ <th><label for="RestrictAdmins"><?php esc_html_e( 'Restrict Plugin Access', 'wp-security-audit-log' ); ?></label></th>
341
  <td>
342
  <fieldset>
343
+ <input type="hidden" id="RestrictAdminsDefaultUser" value="<?php echo esc_attr( wp_get_current_user()->user_login ); ?>"/>
344
  <label for="RestrictAdmins">
345
  <?php $ira = $this->_plugin->settings->IsRestrictAdmins(); ?>
346
+ <input type="checkbox" name="RestrictAdmins" id="RestrictAdmins" <?php checked( $ira ); ?> />
347
  </label>
348
  <br/>
349
  <span class="description">
350
+ <?php esc_html_e( 'If this option is disabled all the administrators on this WordPress have access to manage this plugin.', 'wp-security-audit-log' ); ?><br/>
351
+ <?php echo wp_kses( __( 'By enabling this option only <strong>You</strong> and the users specified in the <strong>Can Manage Plugin</strong> and <strong>Can View Alerts</strong> can configure this plugin or view the alerts in the WordPress audit trail.', 'wp-security-audit-log' ), $this->_plugin->allowed_html_tags ); ?>
352
  </span>
353
  </fieldset>
354
  </td>
355
  </tr>
356
  <!-- Developer Options -->
357
  <tr>
358
+ <th><label><?php esc_html_e( 'Developer Options', 'wp-security-audit-log' ); ?></label></th>
359
  <td>
360
  <fieldset>
361
  <?php $any = $this->_plugin->settings->IsAnyDevOptionEnabled(); ?>
362
+ <a href="javascript:;" style="<?php echo ( $any ) ? 'display: none;' : false; ?>"
363
+ onclick="jQuery(this).hide().next().show();">
364
+ <?php esc_html_e( 'Show Developer Options', 'wp-security-audit-log' ); ?>
365
+ </a>
366
+ <div style="<?php echo ( ! $any ) ? 'display: none;' : false; ?>">
367
  <p style="border-left: 3px solid #FFD000; padding: 2px 8px; margin-left: 6px; margin-bottom: 16px;">
368
+ <?php esc_html_e( 'Only enable these options on testing, staging and development websites. Enabling any of the settings below on LIVE websites may cause unintended side-effects including degraded performance.', 'wp-security-audit-log' ); ?>
369
  </p>
370
  <?php
371
+ foreach ( array(
372
+ WSAL_Settings::OPT_DEV_DATA_INSPECTOR => array(
373
+ __( 'Data Inspector', 'wp-security-audit-log' ),
374
+ __( 'View data logged for each triggered alert.', 'wp-security-audit-log' ),
375
+ ),
376
+ /**
377
+ WSAL_Settings::OPT_DEV_PHP_ERRORS => array(
378
+ __('PHP Errors', 'wp-security-audit-log'),
379
+ __('Enables sensor for alerts generated from PHP.', 'wp-security-audit-log')
380
+ ), */
381
+ WSAL_Settings::OPT_DEV_REQUEST_LOG => array(
382
+ __( 'Request Log', 'wp-security-audit-log' ),
383
+ __( 'Enables logging request to file.', 'wp-security-audit-log' ),
384
+ ),
385
+ /**
386
+ WSAL_Settings::OPT_DEV_BACKTRACE_LOG => array(
387
+ __('Backtrace', 'wp-security-audit-log'),
388
+ __('Log full backtrace for PHP-generated alerts.', 'wp-security-audit-log')
389
+ ), */
390
+ ) as $opt => $info ) {
391
+ ?>
392
+ <label for="devoption_<?php echo esc_attr( $opt ); ?>">
393
+ <input type="checkbox" name="DevOptions[]" id="devoption_<?php echo esc_attr( $opt ); ?>"
394
+ <?php checked( $this->_plugin->settings->IsDevOptionEnabled( $opt ) ); ?>
395
+ value="<?php echo esc_attr( $opt ); ?>">
396
+ <span><?php echo esc_html( $info[0] ); ?></span>
397
+ <!-- Title -->
398
+ <?php if ( isset( $info[1] ) && $info[1] ) : ?>
399
+ <span class="description"> &mdash; <?php echo esc_html( $info[1] ); ?></span>
400
+ <?php endif; ?>
401
+ <!-- Description -->
402
+ </label><br/>
403
+ <?php
404
+ }
405
  ?>
406
  <span class="description">
407
+ <?php esc_html_e( 'The request log file is saved in the /wp-content/uploads/wp-security-audit-log/ directory.', 'wp-security-audit-log' ); ?>
408
  </span>
409
  </div>
410
  </fieldset>
412
  </tr>
413
  <!-- Hide Plugin in Plugins Page -->
414
  <tr>
415
+ <th><label for="Incognito"><?php esc_html_e( 'Hide Plugin in Plugins Page', 'wp-security-audit-log' ); ?></label></th>
416
  <td>
417
  <fieldset>
418
  <label for="Incognito">
419
+ <input type="checkbox" name="Incognito" value="1" id="Incognito" <?php checked( $this->_plugin->settings->IsIncognito() ); ?> />
420
+ <?php esc_html_e( 'Hide', 'wp-security-audit-log' ); ?>
 
421
  </label>
422
  <br/>
423
  <span class="description">
424
+ <?php esc_html_e( 'To manually revert this setting set the value of option wsal-hide-plugin to 0 in the wp_wsal_options table.', 'wp-security-audit-log' ); ?>
425
  </span>
426
  </fieldset>
427
  </td>
428
  </tr>
429
  <!-- Logging -->
430
  <tr>
431
+ <th><label for="Logging"><?php esc_html_e( 'Logging', 'wp-security-audit-log' ); ?></label></th>
432
  <td>
433
  <fieldset>
434
  <label for="Logging">
442
  </label>
443
  <br/>
444
  <span class="description">
445
+ <?php esc_html_e( 'Disable all plugin logging.', 'wp-security-audit-log' ); ?>
446
  </span>
447
  </fieldset>
448
  </td>
449
  </tr>
450
  <!-- Remove Data on Uninstall -->
451
  <tr>
452
+ <th><label for="DeleteData"><?php esc_html_e( 'Remove Data on Uninstall', 'wp-security-audit-log' ); ?></label></th>
453
  <td>
454
  <fieldset>
455
  <label for="DeleteData">
456
+ <input type="checkbox" name="DeleteData" value="1" id="DeleteData" onclick="return delete_confirm(this);"
457
+ <?php checked( $this->_plugin->settings->IsDeleteData() ); ?> />
458
+ <span class="description">
459
+ <?php esc_html_e( 'Check this box if you would like remove all data when the plugin is deleted.', 'wp-security-audit-log' ); ?>
460
+ </span>
461
  </label>
462
  </fieldset>
463
  </td>
469
  <tbody>
470
  <!-- Security Alerts Pruning -->
471
  <?php
472
+ $disabled = '';
473
+ if ( $this->_plugin->settings->IsArchivingEnabled() ) {
474
  $disabled = 'disabled';
475
  ?>
476
  <tr>
477
  <td colspan="2">
478
+ <?php esc_html_e( 'The options below are disabled because you enabled archiving of alerts to the archiving table from', 'wp-security-audit-log' ); ?>&nbsp;<a href="<?php echo esc_url( admin_url( 'admin.php?page=wsal-ext-settings#mirroring' ) ); ?>" target="_blank">here</a>
479
  </td>
480
  </tr>
481
  <?php } ?>
482
  <tr>
483
+ <th><label for="delete1"><?php esc_html_e( 'Security Alerts Pruning', 'wp-security-audit-log' ); ?></label></th>
484
  <td>
485
  <fieldset>
486
+ <?php $text = __( '(eg: 1 month)', 'wp-security-audit-log' ); ?>
487
+ <?php $nbld = ! ($this->_plugin->settings->IsPruningDateEnabled() || $this->_plugin->settings->IsPruningLimitEnabled()); ?>
488
  <label for="delete0">
489
+