WP Security Audit Log - Version 1.0

Version Description

(2014-05-20) = * Complete plugin rewrite making the new version more stable and scalable

  • New Features

    • New Audit Log viewer
    • Auto refresh of security alerts - WordPress administrators do not need to refresh the Audit Log Viewer page to see new alerts
    • Data Inspector reports more insider information about each alert (can be enabled from settings)
    • Sandbox allows developers to execute PHP code for troubleshooting (can be enabled from settings)
    • Request Log that logs all HTTP GET and POST requests done on WordPress (can be enabled from settings)
    • Logging of PHP Errors; ideal for developers who want to monitor WordPress for any errors (can be enabled from settings)
    • New Support and About Us page that you should check out!
  • New WordPress Security Alerts for monitoring themes, WordPress settings, files and much more...

    • Alert 2046: User modified a file using the editor
    • Alert 2047: User changed parent of page
    • Alert 2048: User changed template of page
    • Alert 2049: User set post as sticky
    • Alert 2050: User removed post from Sticky
    • Alert 5005: User installed a new theme
    • Alert 5006: User activated a theme
    • Alert 6004: User upgraded WordPress
    • Alert 6005: User changed the WordPress permalinks
  • New WordPress Developer Alerts

    • Alert 0000: Unknown error
    • Alert 0001: PHP Error
    • Alert 0002: PHP Warning
    • Alert 0003: PHP Notice
    • Alert 0004: PHP Exception
    • Alert 0005: PHP Shutdown Error
  • For more information about what is new and changed in this version of the plugin refer to the WP Security Audit Log release notes.

Download this release

Release Info

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

Code changes from version 0.6.3 to 1.0

Files changed (67) hide show
  1. classes/AbstractLogger.php +14 -0
  2. classes/AbstractSensor.php +34 -0
  3. classes/AbstractView.php +82 -0
  4. classes/Alert.php +43 -0
  5. classes/AlertManager.php +213 -0
  6. classes/ConstantManager.php +89 -0
  7. classes/DB/ActiveRecord.php +428 -0
  8. classes/DB/Meta.php +11 -0
  9. classes/DB/Occurrence.php +212 -0
  10. classes/Loggers/Database.php +63 -0
  11. classes/Nicer.php +186 -0
  12. classes/SensorManager.php +21 -0
  13. classes/Sensors/Content.php +405 -0
  14. classes/Sensors/Files.php +47 -0
  15. classes/Sensors/LogInOut.php +59 -0
  16. classes/Sensors/Multisite.php +79 -0
  17. classes/Sensors/PhpErrors.php +89 -0
  18. classes/Sensors/PluginsThemes.php +212 -0
  19. classes/Sensors/Request.php +42 -0
  20. classes/Sensors/System.php +103 -0
  21. classes/Sensors/UserProfile.php +146 -0
  22. classes/Sensors/Widgets.php +180 -0
  23. classes/Settings.php +366 -0
  24. classes/ViewManager.php +127 -0
  25. classes/Views/About.php +87 -0
  26. classes/Views/AuditLog.php +390 -0
  27. classes/Views/Help.php +100 -0
  28. classes/Views/Sandbox.php +231 -0
  29. classes/Views/Settings.php +245 -0
  30. classes/Views/ToggleAlerts.php +139 -0
  31. classes/WidgetManager.php +59 -0
  32. css/auditlog.css +121 -0
  33. css/nice_r.css +74 -0
  34. css/settings.css +39 -0
  35. defaults.php +144 -0
  36. {res/img → img}/logo-main-menu.png +0 -0
  37. inc/WPPH.php +0 -513
  38. inc/WPPHDatabase.php +0 -385
  39. inc/WPPHEvent.php +0 -2213
  40. inc/WPPHLogger.php +0 -40
  41. inc/WPPHNetwork.php +0 -232
  42. inc/WPPHPost.php +0 -191
  43. inc/WPPHUtil.php +0 -371
  44. inc/wpphFunctions.php +0 -92
  45. inc/wpphSettings.php +0 -34
  46. js/auditlog.js +53 -0
  47. js/nice_r.js +12 -0
  48. js/settings.js +37 -0
  49. pages/about.php +0 -21
  50. pages/alerts.php +0 -190
  51. pages/dashboard.php +0 -165
  52. pages/settings.php +0 -576
  53. pages/support.php +0 -22
  54. readme.txt +65 -24
  55. res/css/styles.base.css +0 -122
  56. res/img/ajax-loader.gif +0 -0
  57. res/img/error-icon.png +0 -0
  58. res/img/page-about-logo.png +0 -0
  59. res/img/page-settings-logo.png +0 -0
  60. res/img/page-support-logo.png +0 -0
  61. res/img/page-viewer-logo.png +0 -0
  62. res/img/success-icon.png +0 -0
  63. res/js/audit-view-model.js +0 -314
  64. res/js/jquery-ck.js +0 -95
  65. res/js/knockout.js +0 -85
  66. uninstall.php +7 -0
  67. wp-security-audit-log.php +219 -180
classes/AbstractLogger.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class WSAL_AbstractLogger {
4
+ /**
5
+ * @var WpSecurityAuditLog
6
+ */
7
+ protected $plugin;
8
+
9
+ public function __construct(WpSecurityAuditLog $plugin){
10
+ $this->plugin = $plugin;
11
+ }
12
+
13
+ public abstract function Log($type, $data = array(), $date = null, $siteid = null, $migrated = false);
14
+ }
classes/AbstractSensor.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class WSAL_AbstractSensor {
4
+ /**
5
+ * @var WpSecurityAuditLog
6
+ */
7
+ protected $plugin;
8
+
9
+ public function __construct(WpSecurityAuditLog $plugin){
10
+ $this->plugin = $plugin;
11
+ }
12
+
13
+ abstract function HookEvents();
14
+
15
+ protected function Log($type, $message, $args){
16
+ $this->plugin->alerts->Trigger($type, array(
17
+ 'Message' => $message,
18
+ 'Context' => $args,
19
+ 'Trace' => debug_backtrace(),
20
+ ));
21
+ }
22
+
23
+ protected function LogError($message, $args){
24
+ $this->Log(0001, $message, $args);
25
+ }
26
+
27
+ protected function LogWarn($message, $args){
28
+ $this->Log(0002, $message, $args);
29
+ }
30
+
31
+ protected function LogInfo($message, $args){
32
+ $this->Log(0003, $message, $args);
33
+ }
34
+ }
classes/AbstractView.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class WSAL_AbstractView {
4
+
5
+ /**
6
+ * @var WpSecurityAuditLog
7
+ */
8
+ protected $_plugin;
9
+
10
+ protected $_wpversion;
11
+
12
+ /**
13
+ * @param WpSecurityAuditLog $plugin
14
+ */
15
+ public function __construct(WpSecurityAuditLog $plugin){
16
+ $this->_plugin = $plugin;
17
+
18
+ // get and store wordpress version
19
+ global $wp_version;
20
+ if(!isset($wp_version))
21
+ $wp_version = get_bloginfo('version');
22
+ $this->_wpversion = floatval($wp_version);
23
+ }
24
+
25
+ /**
26
+ * @return string Return page name (for menu etc).
27
+ */
28
+ abstract public function GetName();
29
+
30
+ /**
31
+ * @return string Return page title.
32
+ */
33
+ abstract public function GetTitle();
34
+
35
+ /**
36
+ * @return string Page icon name.
37
+ */
38
+ abstract public function GetIcon();
39
+
40
+ /**
41
+ * @return int Menu weight, the higher this is, the lower it goes.
42
+ */
43
+ abstract public function GetWeight();
44
+
45
+ /**
46
+ * Renders and outputs the view directly.
47
+ */
48
+ abstract public function Render();
49
+
50
+ /**
51
+ * @return boolean Whether page should appear in menu or not.
52
+ */
53
+ public function IsVisible(){ return true; }
54
+
55
+ /**
56
+ * Used for rendering stuff into head tag.
57
+ */
58
+ public function Header(){}
59
+
60
+ /**
61
+ * Used for rendering stuff in page fotoer.
62
+ */
63
+ public function Footer(){}
64
+
65
+ /**
66
+ * @return string Safe view menu name.
67
+ */
68
+ public function GetSafeViewName(){
69
+ return 'wsal-' . strtolower(
70
+ preg_replace('/[^A-Za-z0-9\-]/', '-', $this->GetName())
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Override this and make it return true to create a shortcut link in plugin page to the view.
76
+ * @return boolean
77
+ */
78
+ public function HasPluginShortcutLink(){
79
+ return false;
80
+ }
81
+
82
+ }
classes/Alert.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class WSAL_Alert {
4
+
5
+ /**
6
+ * Alert type (used when triggering an alert etc).
7
+ * @var integer
8
+ */
9
+ public $type = 0;
10
+
11
+ /**
12
+ * Alert error level (E_* constant).
13
+ * @var integer
14
+ */
15
+ public $code = 0;
16
+
17
+ /**
18
+ * Alert category (alerts are grouped by matching categories).
19
+ * @var string
20
+ */
21
+ public $catg = '';
22
+
23
+ /**
24
+ * Alert description (ie, describes what happens when alert is triggered).
25
+ * @var string
26
+ */
27
+ public $desc = '';
28
+
29
+ /**
30
+ * Alert message (variables between '%' are expanded to values).
31
+ * @var string
32
+ */
33
+ public $mesg = '';
34
+
35
+ public function __construct($type = 0, $code = 0, $catg = '', $desc = '', $mesg = '') {
36
+ $this->type = $type;
37
+ $this->code = $code;
38
+ $this->catg = $catg;
39
+ $this->desc = $desc;
40
+ $this->mesg = $mesg;
41
+ }
42
+
43
+ }
classes/AlertManager.php ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class WSAL_AlertManager {
4
+
5
+ /**
6
+ * @var WSAL_Alert[]
7
+ */
8
+ protected $_alerts = array();
9
+
10
+ /**
11
+ * @var WSAL_AbstractLogger[]
12
+ */
13
+ protected $_loggers = array();
14
+
15
+ /**
16
+ * @var WpSecurityAuditLog
17
+ */
18
+ protected $plugin;
19
+
20
+ /**
21
+ * Create new AlertManager instance.
22
+ * @param WpSecurityAuditLog $plugin
23
+ */
24
+ public function __construct(WpSecurityAuditLog $plugin){
25
+ $this->plugin = $plugin;
26
+ foreach(glob(dirname(__FILE__) . '/Loggers/*.php') as $file){
27
+ $class = $plugin->GetClassFileClassName($file);
28
+ $this->_loggers[] = new $class($plugin);
29
+ }
30
+
31
+ add_action('shutdown', array($this, '_CommitPipeline'));
32
+ }
33
+
34
+ /**
35
+ * Contains a list of alerts to trigger.
36
+ * @var array
37
+ */
38
+ protected $_pipeline = array();
39
+
40
+ /**
41
+ * Trigger an alert.
42
+ * @param integer $type Alert type.
43
+ * @param array $data Alert data.
44
+ */
45
+ public function Trigger($type, $data = array(), $delayed = false){
46
+ if($delayed){
47
+ $this->TriggerIf($type, $data, null);
48
+ }else{
49
+ $this->_CommitItem($type, $data, null);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Trigger only if a condition is met at the end of request.
55
+ * @param integer $type Alert type ID.
56
+ * @param array $data Alert data.
57
+ * @param callable $cond A future condition callback (receives an object of type WSAL_AlertManager as parameter).
58
+ */
59
+ public function TriggerIf($type, $data, $cond = null){
60
+ $this->_pipeline[] = array(
61
+ 'type' => $type,
62
+ 'data' => $data,
63
+ 'cond' => $cond,
64
+ );
65
+ }
66
+
67
+ /**
68
+ * @internal Commit an alert now.
69
+ */
70
+ protected function _CommitItem($type, $data, $cond){
71
+ if(!$cond || !!call_user_func($cond, $this)){
72
+ if($this->IsEnabled($type)){
73
+ if(isset($this->_alerts[$type])){
74
+ // ok, convert alert to a log entry
75
+ $this->Log($type, $data);
76
+ }else{
77
+ // in general this shouldn't happen, but it could, so we handle it here :)
78
+ throw new Exception('Alert with code "' . $type . '" has not be registered.');
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * @internal Runs over triggered alerts in pipeline and passes them to loggers.
86
+ */
87
+ public function _CommitPipeline(){
88
+ foreach($this->_pipeline as $item)
89
+ $this->_CommitItem($item['type'], $item['data'], $item['cond']);
90
+ }
91
+
92
+ /**
93
+ * @param integer $type Alert type ID.
94
+ * @return boolean True if at the end of request an alert of this type will be triggered.
95
+ */
96
+ public function WillTrigger($type){
97
+ foreach($this->_pipeline as $item)
98
+ if($item['type'] == $type)
99
+ return true;
100
+ return false;
101
+ }
102
+
103
+ /**
104
+ * Register an alert type.
105
+ * @param array $info Array of [type, code, category, description, message] respectively.
106
+ */
107
+ public function Register($info){
108
+ if(func_num_args() == 1){
109
+ // handle single item
110
+ list($type, $code, $catg, $desc, $mesg) = $info;
111
+ if(isset($this->_alerts[$type]))
112
+ throw new Exception("Alert $type already registered with Alert Manager.");
113
+ $this->_alerts[$type] = new WSAL_Alert($type, $code, $catg, $desc, $mesg);
114
+ }else{
115
+ // handle multiple items
116
+ foreach(func_get_args() as $arg)
117
+ $this->Register($arg);
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Register a whole group of items.
123
+ * @param array $groups An array with group name as the index and an array of group items as the value.
124
+ * Item values is an array of [type, code, description, message] respectively.
125
+ */
126
+ public function RegisterGroup($groups){
127
+ foreach($groups as $name => $group){
128
+ foreach($group as $item){
129
+ list($type, $code, $desc, $mesg) = $item;
130
+ $this->Register(array($type, $code, $name, $desc, $mesg));
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Returns whether alert of type $type is enabled or not.
137
+ * @param integer $type Alert type.
138
+ * @return boolean True if enabled, false otherwise.
139
+ */
140
+ public function IsEnabled($type){
141
+ return !in_array($type, $this->GetDisabledAlerts());
142
+ }
143
+
144
+ /**
145
+ * Disables a set of alerts by type.
146
+ * @param int[] $types Alert type codes to be disabled.
147
+ */
148
+ public function SetDisabledAlerts($types){
149
+ $this->plugin->settings->SetDisabledAlerts($types);
150
+ }
151
+
152
+ /**
153
+ * @return int[] Returns an array of disabled alerts' type code.
154
+ */
155
+ public function GetDisabledAlerts(){
156
+ return $this->plugin->settings->GetDisabledAlerts();
157
+ }
158
+
159
+ /**
160
+ * Converts an Alert into a Log entry (by invoking loggers).
161
+ * You should not call this method directly.
162
+ * @param integer $type Alert type.
163
+ * @param array $data Misc alert data.
164
+ */
165
+ protected function Log($type, $data = array()){
166
+ if(!isset($data['ClientIP']))
167
+ $data['ClientIP'] = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
168
+ if(!isset($data['UserAgent']))
169
+ $data['UserAgent'] = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
170
+ if(!isset($data['CurrentUserID']))
171
+ $data['CurrentUserID'] = function_exists('get_current_user_id') ? get_current_user_id() : 0;
172
+
173
+ foreach($this->_loggers as $logger)
174
+ $logger->Log($type, $data);
175
+ }
176
+
177
+ /**
178
+ * Return alert given alert type.
179
+ * @param integer $type Alert type.
180
+ * @param mixed $default Returned if alert is not found.
181
+ * @return WSAL_Alert
182
+ */
183
+ public function GetAlert($type, $default = null){
184
+ foreach($this->_alerts as $alert)
185
+ if($alert->type == $type)
186
+ return $alert;
187
+ return $default;
188
+ }
189
+
190
+ /**
191
+ * Returns all supported alerts.
192
+ * @return WSAL_Alert[]
193
+ */
194
+ public function GetAlerts(){
195
+ return $this->_alerts;
196
+ }
197
+
198
+ /**
199
+ * Returns all supported alerts.
200
+ * @return array
201
+ */
202
+ public function GetCategorizedAlerts(){
203
+ $result = array();
204
+ foreach($this->_alerts as $alert){
205
+ if(!isset($result[$alert->catg]))
206
+ $result[$alert->catg] = array();
207
+ $result[$alert->catg][] = $alert;
208
+ }
209
+ ksort($result);
210
+ return $result;
211
+ }
212
+
213
+ }
classes/ConstantManager.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_ConstantManager {
4
+
5
+ protected $_constants = array();
6
+
7
+ /**
8
+ * Use an existing PHP constant.
9
+ * @param string $name Constant name.
10
+ * @param string $description Constant description.
11
+ */
12
+ public function UseConstant($name, $description = ''){
13
+ $this->_constants[] = (object)array(
14
+ 'name' => $name,
15
+ 'value' => constant($name),
16
+ 'description' => $description,
17
+ );
18
+ }
19
+
20
+ /**
21
+ * Add new PHP constant.
22
+ * @param string $name Constant name.
23
+ * @param integer|string $value Constant value.
24
+ * @param string $description Constant description.
25
+ */
26
+ public function AddConstant($name, $value, $description = ''){
27
+
28
+ // check for constant conflict and define new one if required
29
+ if(defined($name) && constant($name) !== $value){
30
+ throw new Exception('Constant already defined with a different value.');
31
+ }else{
32
+ define($name, $value);
33
+ }
34
+
35
+ // add constant to da list.
36
+ $this->UseConstant($name, $description);
37
+
38
+ }
39
+
40
+ /**
41
+ * Add multiple constants in one go.
42
+ * @param array $items Array of arrays with name, value, description pairs.
43
+ */
44
+ public function AddConstants($items){
45
+ foreach($items as $item)
46
+ $this->AddConstant(
47
+ $item['name'],
48
+ $item['value'],
49
+ $item['description']
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Use multiple constants in one go.
55
+ * @param array $items Array of arrays with name, description pairs.
56
+ */
57
+ public function UseConstants($items){
58
+ foreach($items as $item)
59
+ $this->UseConstant(
60
+ $item['name'],
61
+ $item['description']
62
+ );
63
+ }
64
+
65
+ /**
66
+ * Get constant details by a particular detail.
67
+ * @param string $what The type of detail: 'name', 'value'
68
+ * @param mixed $value The detail expected value.
69
+ * @return mixed Either constant details (props: name, value, description) or $default if not found.
70
+ */
71
+ public function GetConstantBy($what, $value, $default = null){
72
+
73
+ // make sure we do have some constants...
74
+ if(count($this->_constants)){
75
+
76
+ // make sure that constants do have a $what property
77
+ if(!isset($this->_constants[0]->$what))
78
+ throw new Exception('Unexpected detail type "' . $what . '".');
79
+
80
+ // return constant match the property value
81
+ foreach($this->_constants as $constant)
82
+ if($constant->$what == $value)
83
+ return $constant;
84
+
85
+ }
86
+
87
+ return $default;
88
+ }
89
+ }
classes/DB/ActiveRecord.php ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ abstract class WSAL_DB_ActiveRecord {
4
+
5
+ /**
6
+ * Contains table name, override as required.
7
+ * @var string
8
+ */
9
+ protected $_table = '';
10
+
11
+ /**
12
+ * Contains primary key column name, override as required.
13
+ * @var string
14
+ */
15
+ protected $_idkey = '';
16
+
17
+ const STATE_UNKNOWN = 'unknown';
18
+ const STATE_CREATED = 'created';
19
+ const STATE_UPDATED = 'updated';
20
+ const STATE_DELETED = 'deleted';
21
+ const STATE_LOADED = 'loaded';
22
+
23
+ protected $_state = self::STATE_UNKNOWN;
24
+
25
+ public function __construct($data = null) {
26
+ if(!$this->_table)
27
+ throw new Exception('Class "' . __CLASS__ . '" requires "_table" to be set.');
28
+ if(!$this->_idkey)
29
+ throw new Exception('Class "' . __CLASS__ . '" requires "_idkey" to be set.');
30
+ if(!is_null($data)){
31
+ $this->LoadData($data);
32
+ $this->_state = self::STATE_LOADED;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * @return string Must return SQL for creating table.
38
+ */
39
+ protected function _GetInstallQuery(){
40
+ global $wpdb;
41
+
42
+ $copy = get_class($this);
43
+ $copy = new $copy();
44
+
45
+ $sql = 'CREATE TABLE ' . $this->GetTable() . ' (' . PHP_EOL;
46
+ foreach($this->GetColumns() as $key) {
47
+ switch(true) {
48
+ case $key == $copy->_idkey:
49
+ $sql .= $key . ' BIGINT NOT NULL AUTO_INCREMENT,'.PHP_EOL;
50
+ break;
51
+ case is_integer($copy->$key):
52
+ $sql .= $key . ' BIGINT NOT NULL,'.PHP_EOL;
53
+ break;
54
+ case is_float($copy->$key):
55
+ $sql .= $key . ' FLOAT NOT NULL,'.PHP_EOL;
56
+ break;
57
+ case is_string($copy->$key):
58
+ $sql .= $key . ' TEXT NOT NULL,'.PHP_EOL;
59
+ break;
60
+ case is_bool($copy->$key):
61
+ $sql .= $key . ' BIT NOT NULL,'.PHP_EOL;
62
+ break;
63
+ case is_array($copy->$key):
64
+ case is_object($copy->$key):
65
+ $sql .= $key . ' LONGTEXT NOT NULL,'.PHP_EOL;
66
+ break;
67
+ }
68
+ }
69
+ $sql .= 'CONSTRAINT PK_' . $this->GetTable().'_'.$this->_idkey
70
+ . ' PRIMARY KEY (' . $this->_idkey . ')' . PHP_EOL
71
+ . ' )';
72
+ if ( ! empty($wpdb->charset) )
73
+ $sql .= ' DEFAULT CHARACTER SET ' . $wpdb->charset;
74
+ if ( ! empty($wpdb->collate) )
75
+ $sql .= ' COLLATE ' . $wpdb->collate;
76
+ return $sql;
77
+ }
78
+
79
+ /**
80
+ * @return string Must return SQL for removing table (at a minimum, it should be ` 'DROP TABLE ' . $this->_table `).
81
+ */
82
+ protected function _GetUninstallQuery(){
83
+ return 'DROP TABLE ' . $this->GetTable();
84
+ }
85
+
86
+ /**
87
+ * A wrapper for JSON encoding that fixes potential issues.
88
+ * @param mixed $data The data to encode.
89
+ * @return string JSON string.
90
+ */
91
+ protected function _JsonEncode($data){
92
+ return @json_encode($data);
93
+ }
94
+
95
+ /**
96
+ * A wrapper for JSON encoding that fixes potential issues.
97
+ * @param string $data The JSON string to decode.
98
+ * @return mixed Decoded data.
99
+ */
100
+ protected function _JsonDecode($data){
101
+ return @json_decode($data);
102
+ }
103
+
104
+ /**
105
+ * @return string Returns table name.
106
+ */
107
+ public function GetTable(){
108
+ global $wpdb;
109
+ return $wpdb->base_prefix . $this->_table;
110
+ }
111
+
112
+ /**
113
+ * @return array Returns this records' columns.
114
+ */
115
+ public function GetColumns(){
116
+ if(!isset($this->_column_cache)){
117
+ $this->_column_cache = array();
118
+ foreach(array_keys(get_object_vars($this)) as $col)
119
+ if(trim($col) && $col[0] != '_')
120
+ $this->_column_cache[] = $col;
121
+ }
122
+ return $this->_column_cache;
123
+ }
124
+
125
+ /**
126
+ * @return boolean Returns whether table structure is installed or not.
127
+ */
128
+ public function IsInstalled(){
129
+ global $wpdb;
130
+ $sql = 'SHOW TABLES LIKE "' . $this->GetTable() . '"';
131
+ return $wpdb->get_var($sql) == $this->GetTable();
132
+ }
133
+
134
+ /**
135
+ * Install this ActiveRecord structure into DB.
136
+ */
137
+ public function Install(){
138
+ if(!$this->IsInstalled()) {
139
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
140
+ dbDelta($this->_GetInstallQuery());
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Remove this ActiveRecord structure into DB.
146
+ */
147
+ public function Uninstall(){
148
+ if($this->IsInstalled()) {
149
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
150
+ dbDelta($this->_GetUninstallQuery());
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Save record to DB.
156
+ * @return integer|boolean Either the number of modified/inserted rows or false on failure.
157
+ */
158
+ public function Save(){
159
+ $this->_state = self::STATE_UNKNOWN;
160
+ global $wpdb;
161
+ $copy = get_class($this);
162
+ $copy = new $copy;
163
+ $data = array();
164
+ $format = array();
165
+ foreach($this->GetColumns() as $key){
166
+ $val = $this->$key;
167
+ $deffmt = '%s';
168
+ if(is_int($copy->$key))$deffmt = '%d';
169
+ if(is_float($copy->$key))$deffmt = '%f';
170
+ if(is_array($copy->$key) || is_object($copy->$key)){
171
+ $data[$key] = $this->_JsonEncode($val);
172
+ }else{
173
+ $data[$key] = $val;
174
+ }
175
+ $format[] = $deffmt;
176
+ }
177
+ $result = $wpdb->replace($this->GetTable(), $data, $format);
178
+ if($wpdb->insert_id){
179
+ $this->{$this->_idkey} = $wpdb->insert_id;
180
+ if($result !== false)
181
+ $this->_state = self::STATE_CREATED;
182
+ }else{
183
+ if($result !== false)
184
+ $this->_state = self::STATE_UPDATED;
185
+ }
186
+ return $result;
187
+ }
188
+
189
+ /**
190
+ * Load record from DB.
191
+ * @param string $cond (Optional) Load condition.
192
+ * @param array $args (Optional) Load condition arguments.
193
+ */
194
+ public function Load($cond = '%d', $args = array(1)){
195
+ global $wpdb;
196
+
197
+ $this->_state = self::STATE_UNKNOWN;
198
+
199
+ $sql = $wpdb->prepare('SELECT * FROM '.$this->GetTable().' WHERE '.$cond, $args);
200
+ $data = $wpdb->get_row($sql, ARRAY_A);
201
+
202
+ if(!is_null($data)){
203
+ $this->LoadData($data);
204
+ $this->_state = self::STATE_LOADED;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Load object data from variable.
210
+ * @param array|object $data Data array or object.
211
+ */
212
+ public function LoadData($data){
213
+ $copy = get_class($this);
214
+ $copy = new $copy;
215
+ foreach((array)$data as $key => $val){
216
+ if(isset($copy->$key)){
217
+ switch(true){
218
+ case is_array($copy->$key):
219
+ case is_object($copy->$key):
220
+ $this->$key = $this->_JsonDecode($val);
221
+ break;
222
+ case is_int($copy->$key):
223
+ $this->$key = (int)$val;
224
+ break;
225
+ case is_float($copy->$key):
226
+ $this->$key = (float)$val;
227
+ break;
228
+ case is_bool($copy->$key):
229
+ $this->$key = (bool)$val;
230
+ break;
231
+ case is_string($copy->$key):
232
+ $this->$key = (string)$val;
233
+ break;
234
+ default:
235
+ throw new Exception('Unsupported type "'.gettype($copy->$key).'"');
236
+ }
237
+ }
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Delete DB record.
243
+ * @return int|boolean Either the amount of deleted rows or False on error.
244
+ */
245
+ public function Delete(){
246
+ global $wpdb;
247
+
248
+ $this->_state = self::STATE_UNKNOWN;
249
+
250
+ $result = $wpdb->delete(
251
+ $this->GetTable(),
252
+ array($this->_idkey => $this->{$this->_idkey})
253
+ );
254
+
255
+ if($result !== false)
256
+ $this->_state = self::STATE_DELETED;
257
+
258
+ return $result;
259
+ }
260
+
261
+ /**
262
+ * Load multiple records from DB.
263
+ * @param string $cond (Optional) Load condition.
264
+ * @param array $args (Optional) Load condition arguments.
265
+ * @return self[] List of loaded records.
266
+ */
267
+ public static function LoadMulti($cond = '%d', $args = array(1)){
268
+ global $wpdb;
269
+ $class = get_called_class();
270
+ $result = array();
271
+ $temp = new $class();
272
+ $sql = $wpdb->prepare('SELECT * FROM ' . $temp->GetTable() . ' WHERE '.$cond, $args);
273
+ foreach($wpdb->get_results($sql, ARRAY_A) as $data){
274
+ $result[] = new $class($data);
275
+ }
276
+ return $result;
277
+ }
278
+
279
+ /**
280
+ * Load multiple records from DB and call a callback for each record.
281
+ * This function is very memory-efficient, it doesn't load records in bulk.
282
+ * @param callable $callback The callback to invoke.
283
+ * @param string $cond (Optional) Load condition.
284
+ * @param array $args (Optional) Load condition arguments.
285
+ */
286
+ public static function LoadAndCallForEach($callback, $cond = '%d', $args = array(1)){
287
+ global $wpdb;
288
+ $class = get_called_class();
289
+ $temp = new $class();
290
+ $sql = $wpdb->prepare('SELECT * FROM ' . $temp->GetTable() . ' WHERE '.$cond, $args);
291
+ foreach($wpdb->get_results($sql, ARRAY_A) as $data){
292
+ call_user_func($callback, new $class($data));
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Count records in the DB matching a condition.
298
+ * If no parameters are given, this counts the number of records in the DB table.
299
+ * @param string $cond (Optional) Query condition.
300
+ * @param array $args (Optional) Condition arguments.
301
+ */
302
+ public static function Count($cond = '%d', $args = array(1)){
303
+ global $wpdb;
304
+ $class = get_called_class();
305
+ $temp = new $class();
306
+ $sql = $wpdb->prepare('SELECT COUNT(*) FROM ' . $temp->GetTable() . ' WHERE '.$cond, $args);
307
+ return (int)$wpdb->get_var($sql);
308
+ }
309
+
310
+ /**
311
+ * Similar to LoadMulti but allows the use of a full SQL query.
312
+ * @param string $query Full SQL query.
313
+ * @param array $args (Optional) Query arguments.
314
+ * @return self[] List of loaded records.
315
+ */
316
+ public static function LoadMultiQuery($query, $args = array()){
317
+ global $wpdb;
318
+ $class = get_called_class();
319
+ $result = array();
320
+ $sql = $wpdb->prepare($query, $args);
321
+ foreach($wpdb->get_results($sql, ARRAY_A) as $data){
322
+ $result[] = new $class($data);
323
+ }
324
+ return $result;
325
+ }
326
+
327
+ /**
328
+ * Install all DB tables.
329
+ */
330
+ public static function InstallAll(){
331
+ $plugin = WpSecurityAuditLog::GetInstance();
332
+ foreach(glob(dirname(__FILE__) . '/*.php') as $file){
333
+ $class = $plugin->GetClassFileClassName($file);
334
+ if($class != __CLASS__){
335
+ $class = new $class();
336
+ $class->Install();
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Uninstall all DB tables.
343
+ */
344
+ public static function UninstallAll(){
345
+ $plugin = WpSecurityAuditLog::GetInstance();
346
+ foreach(glob(dirname(__FILE__) . '/*.php') as $file){
347
+ $class = $plugin->GetClassFileClassName($file);
348
+ if($class != __CLASS__){
349
+ $class = new $class();
350
+ $class->Uninstall();
351
+ }
352
+ }
353
+ }
354
+
355
+ /**
356
+ * @return boolean
357
+ */
358
+ public function IsLoaded(){
359
+ return $this->_state == self::STATE_LOADED;
360
+ }
361
+
362
+ /**
363
+ * @return boolean
364
+ */
365
+ public function IsSaved(){
366
+ return $this->_state == self::STATE_CREATED
367
+ || $this->_state == self::STATE_UPDATED;
368
+ }
369
+
370
+ /**
371
+ * @return boolean
372
+ */
373
+ public function IsCreated(){
374
+ return $this->_state == self::STATE_CREATED;
375
+ }
376
+
377
+ /**
378
+ * @return boolean
379
+ */
380
+ public function IsUpdated(){
381
+ return $this->_state == self::STATE_UPDATED;
382
+ }
383
+
384
+ /**
385
+ * @return boolean
386
+ */
387
+ public function IsDeleted(){
388
+ return $this->_state == self::STATE_DELETED;
389
+ }
390
+
391
+ protected static $_cache = array();
392
+
393
+ /**
394
+ * Load ActiveRecord from DB or cache.
395
+ * @param string $target ActiveRecord class name.
396
+ * @param string $query Load condition.
397
+ * @param array $args Arguments used in condition.
398
+ * @return WSAL_DB_ActiveRecord
399
+ */
400
+ protected static function CacheLoad($target, $query, $args){
401
+ $index = $target . '::' . vsprintf($query, $args);
402
+ if(!isset(self::$_cache[$index])){
403
+ self::$_cache[$index] = new $target();
404
+ self::$_cache[$index]->Load($query, $args);
405
+ }
406
+ return self::$_cache[$index];
407
+ }
408
+
409
+ /**
410
+ * Remove ActiveRecord cache.
411
+ * @param string $target ActiveRecord class name.
412
+ * @param string $query Load condition.
413
+ * @param array $args Arguments used in condition.
414
+ */
415
+ protected static function CacheRemove($target, $query, $args){
416
+ $index = $target . '::' . sprintf($query, $args);
417
+ if(!isset(self::$_cache[$index])){
418
+ unset(self::$_cache[$index]);
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Clear the cache.
424
+ */
425
+ protected static function CacheClear(){
426
+ self::$_cache = array();
427
+ }
428
+ }
classes/DB/Meta.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_DB_Meta extends WSAL_DB_ActiveRecord {
4
+ protected $_table = 'wsal_metadata';
5
+ protected $_idkey = 'id';
6
+
7
+ public $id = 0;
8
+ public $occurrence_id = 0;
9
+ public $name = '';
10
+ public $value = array(); // force mixed type
11
+ }
classes/DB/Occurrence.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_DB_Occurrence extends WSAL_DB_ActiveRecord {
4
+ protected $_table = 'wsal_occurrences';
5
+ protected $_idkey = 'id';
6
+
7
+ public $id = 0;
8
+ public $site_id = 0;
9
+ public $alert_id = 0;
10
+ public $created_on = 0;
11
+ public $is_read = false;
12
+ public $is_migrated = false;
13
+
14
+ protected $_meta;
15
+
16
+ /**
17
+ * Returns all meta data related to this event.
18
+ * @return WSAL_DB_Meta[]
19
+ */
20
+ public function GetMeta(){
21
+ if(!isset($this->_meta)){
22
+ $this->_meta = WSAL_DB_Meta::LoadMulti('occurrence_id = %d', array($this->id));
23
+ }
24
+ return $this->_meta;
25
+ }
26
+
27
+ /**
28
+ * Loads a meta item given its name.
29
+ * @param string $name Meta name.
30
+ * @return \WSAL_DB_Meta The meta item, be sure to checked if it was loaded successfully.
31
+ */
32
+ public function GetNamedMeta($name){
33
+ $meta = new WSAL_DB_Meta();
34
+ $meta->Load('occurrence_id = %d AND name = %s', array($this->id, $name));
35
+ return $meta;
36
+ }
37
+
38
+ /**
39
+ * 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.
40
+ * @param array $names List of meta names.
41
+ * @return \WSAL_DB_Meta The first meta item that exists.
42
+ */
43
+ public function GetFirstNamedMeta($names){
44
+ $meta = new WSAL_DB_Meta();
45
+ $query = '(' . str_repeat('name = %s OR ', count($names)).'0)';
46
+ $query = 'occurrence_id = %d AND ' . $query . ' LIMIT 1';
47
+ array_unshift($names, $this->id); // prepend args with occurrence id
48
+ $meta->Load($query, $names);
49
+ return $meta->IsLoaded() ? $meta : null;
50
+ }
51
+
52
+ /**
53
+ * Returns the alert related to this occurrence.
54
+ * @return WSAL_Alert
55
+ */
56
+ public function GetAlert(){
57
+ return WpSecurityAuditLog::GetInstance()->alerts->GetAlert($this->alert_id);
58
+ }
59
+
60
+ /**
61
+ * Returns the value of a meta item.
62
+ * @param string $name Name of meta item.
63
+ * @param mixed $default Default value returned when meta does not exist.
64
+ * @return mixed The value, if meta item does not exist $default returned.
65
+ */
66
+ public function GetMetaValue($name, $default = array()){
67
+ $meta = $this->GetNamedMeta($name);
68
+ return $meta->IsLoaded() ? $meta->value : $default;
69
+ }
70
+
71
+ /**
72
+ * Set the value of a meta item (creates or updates meta item).
73
+ * @param string $name Meta name.
74
+ * @param mixed $value Meta value.
75
+ */
76
+ public function SetMetaValue($name, $value){
77
+ $meta = $this->GetNamedMeta($name);
78
+ $meta->occurrence_id = $this->id;
79
+ $meta->name = $name;
80
+ $meta->value = $value;
81
+ $meta->Save();
82
+ }
83
+
84
+ /**
85
+ * Returns a key-value pair of meta data.
86
+ * @return array
87
+ */
88
+ public function GetMetaArray(){
89
+ $result = array();
90
+ foreach($this->GetMeta() as $meta)
91
+ $result[$meta->name] = $meta->value;
92
+ return $result;
93
+ }
94
+
95
+ /**
96
+ * Creates or updates all meta data passed as an array of meta-key/meta-value pairs.
97
+ * @param array $data New meta data.
98
+ */
99
+ public function SetMeta($data){
100
+ foreach((array)$data as $key => $val)
101
+ $this->SetMetaValue($key, $val);
102
+ }
103
+
104
+ /**
105
+ * Retrieves a value for a particular meta variable expression.
106
+ * @param string $expr Expression, eg: User->Name looks for a Name property for meta named User.
107
+ * @return mixed The value nearest to the expression.
108
+ */
109
+ protected function GetMetaExprValue($expr){
110
+ // TODO Handle function calls (and methods?)
111
+ $expr = explode('->', $expr);
112
+ $meta = array_shift($expr);
113
+ $meta = $this->GetMetaValue($meta, null);
114
+ foreach($expr as $part){
115
+ if(is_scalar($meta) || is_null($meta))return $meta; // this isn't 100% correct
116
+ $meta = is_array($meta) ? $meta[$part] : $meta->$part;
117
+ }
118
+ return is_scalar($meta) ? (string)$meta : var_export($meta, true);
119
+ }
120
+
121
+ /**
122
+ * Expands a message with variables by replacing variables with meta data values.
123
+ * @param string $mesg The original message.
124
+ * @param callable|null $metaFormatter (Optional) Callback for formatting meta values.
125
+ * @param string $afterMeta (Optional) Some text to put after meta values.
126
+ * @return string The expanded message.
127
+ */
128
+ protected function GetFormattedMesg($origMesg, $metaFormatter = null){
129
+ // tokenize message with regex
130
+ $mesg = preg_split('/(%.*?%)/', (string)$origMesg, -1, PREG_SPLIT_DELIM_CAPTURE);
131
+ if(!is_array($mesg))return (string)$origMesg;
132
+ // handle tokenized message
133
+ foreach($mesg as $i=>$token){
134
+ // handle escaped percent sign
135
+ if($token == '%%'){
136
+ $mesg[$i] = '%';
137
+ }else
138
+ // handle complex expressions
139
+ if(substr($token, 0, 1) == '%' && substr($token, -1, 1) == '%'){
140
+ $mesg[$i] = $this->GetMetaExprValue(substr($token, 1, -1));
141
+ if($metaFormatter)$mesg[$i] = call_user_func($metaFormatter, $token, $mesg[$i]);
142
+ }
143
+ }
144
+ // compact message and return
145
+ return implode('', $mesg);
146
+ }
147
+
148
+ /**
149
+ * @param callable|null $metaFormatter (Optional) Meta formatter callback.
150
+ * @return string Full-formatted message.
151
+ */
152
+ public function GetMessage($metaFormatter = null){
153
+ if(!isset($this->_cachedmessage)){
154
+ // get correct message entry
155
+ if($this->is_migrated){
156
+ $this->_cachedmessage = $this->GetMetaValue('MigratedMesg', false);
157
+ }
158
+ if(!$this->is_migrated || !$this->_cachedmessage){
159
+ $this->_cachedmessage = $this->GetAlert()->mesg;
160
+ }
161
+ // fill variables in message
162
+ $this->_cachedmessage = $this->GetFormattedMesg($this->_cachedmessage, $metaFormatter);
163
+ }
164
+ return $this->_cachedmessage;
165
+ }
166
+
167
+ /**
168
+ * Returns newest unique occurrences.
169
+ * @param integer $limit Maximum limit.
170
+ * @return WSAL_DB_Occurrence[]
171
+ */
172
+ public static function GetNewestUnique($limit = PHP_INT_MAX){
173
+ $temp = new self();
174
+ return self::LoadMultiQuery('
175
+ SELECT *, COUNT(alert_id) as count
176
+ FROM (
177
+ SELECT *
178
+ FROM ' . $temp->GetTable() . '
179
+ ORDER BY created_on DESC
180
+ ) AS temp_table
181
+ GROUP BY alert_id
182
+ LIMIT %d
183
+ ', array($limit));
184
+ }
185
+
186
+ /**
187
+ * Delete occurrence as well as associated meta data.
188
+ * @return boolean True on success, false on failure.
189
+ */
190
+ public function Delete(){
191
+ foreach($this->GetMeta() as $meta)$meta->Delete();
192
+ return parent::Delete();
193
+ }
194
+
195
+ public function GetUsername(){
196
+ $meta = $this->GetFirstNamedMeta(array('Username', 'CurrentUserID'));
197
+ if($meta){
198
+ switch(true){
199
+ case $meta->name == 'Username':
200
+ return $meta->value;
201
+ case $meta->name == 'CurrentUserID':
202
+ return ($data = get_userdata($meta->value)) ? $data->user_login : null;
203
+ }
204
+ }
205
+ return null;
206
+ }
207
+
208
+ public function GetSourceIP(){
209
+ return $this->GetMetaValue('ClientIP', '');
210
+ }
211
+
212
+ }
classes/Loggers/Database.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
4
+
5
+ public function __construct(WpSecurityAuditLog $plugin) {
6
+ parent::__construct($plugin);
7
+ $plugin->AddCleanupHook(array($this, 'CleanUp'));
8
+ }
9
+
10
+ public function Log($type, $data = array(), $date = null, $siteid = null, $migrated = false) {
11
+
12
+ // use today's date if not set up
13
+ if(is_null($date))$date = current_time('timestamp');
14
+
15
+ // create new occurrence
16
+ $occ = new WSAL_DB_Occurrence();
17
+ $occ->is_migrated = $migrated;
18
+ $occ->created_on = $date;
19
+ $occ->alert_id = $type;
20
+ $occ->site_id = !is_null($siteid) ? $siteid
21
+ : (function_exists('get_current_blog_id') ? get_current_blog_id() : 0);
22
+ $occ->Save();
23
+
24
+ // set up meta data
25
+ $occ->SetMeta($data);
26
+ }
27
+
28
+ public function CleanUp() {
29
+ $now = current_time('timestamp');
30
+ $max_count = $this->plugin->settings->GetPruningLimit();
31
+ $max_sdate = $this->plugin->settings->GetPruningDate();
32
+ $max_stamp = $now - (strtotime($max_sdate) - $now);
33
+ $cnt_items = WSAL_DB_Occurrence::Count();
34
+ if($cnt_items == $max_count)return;
35
+ $max_items = max(($cnt_items - $max_count) + 1, 0);
36
+
37
+ $is_date_e = true;
38
+ $is_limt_e = true;
39
+
40
+ switch(true){
41
+ case $is_date_e && $is_limt_e:
42
+ $cond = 'created_on < %d ORDER BY created_on ASC LIMIT %d';
43
+ $args = array($max_stamp, $max_items);
44
+ break;
45
+ case $is_date_e && !$is_limt_e:
46
+ $cond = 'created_on < %d';
47
+ $args = array($max_stamp);
48
+ break;
49
+ case !$is_date_e && $is_limt_e:
50
+ $cond = '1 ORDER BY created_on ASC LIMIT %d';
51
+ $args = array($max_items);
52
+ break;
53
+ }
54
+ if(!isset($cond))return;
55
+
56
+ $items = WSAL_DB_Occurrence::LoadMulti($cond, $args);
57
+ if(!count($items))return;
58
+
59
+ foreach($items as $item)$item->Delete();
60
+ do_action('wsal_prune', $items, vsprintf($cond, $args));
61
+ }
62
+
63
+ }
classes/Nicer.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
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
+ protected $value;
14
+
15
+ /**
16
+ * Allows modification of CSS class prefix.
17
+ * @var string
18
+ */
19
+ public $css_class = 'nice_r';
20
+
21
+ /**
22
+ * Allows modification of HTML id prefix.
23
+ * @var string
24
+ */
25
+ public $html_id = 'nice_r_v';
26
+
27
+ /**
28
+ * Allows modification of JS function used to toggle sections.
29
+ * @var string
30
+ */
31
+ public $js_func = 'nice_r_toggle';
32
+
33
+ /**
34
+ * Since PHP does not support private constants, we'll have to settle for private static fields.
35
+ * @var string
36
+ */
37
+ protected static $BEEN_THERE = '__NICE_R_INFINITE_RECURSION_PROTECT__';
38
+
39
+ /**
40
+ * Constructs new renderer instance.
41
+ * @param mixed $value The value to inspect and render.
42
+ */
43
+ public function __construct($value){
44
+ $this->value = $value;
45
+ }
46
+
47
+ /**
48
+ * Generates the inspector HTML and returns it as a string.
49
+ * @return string Generated HTML.
50
+ */
51
+ public function generate(){
52
+ return $this->_generate_value($this->value, $this->css_class);
53
+ }
54
+
55
+ /**
56
+ * Renders the inspector HTML directly to the browser.
57
+ */
58
+ public function render(){
59
+ echo $this->generate();
60
+ }
61
+
62
+ /**
63
+ * Converts a string to HTML, encoding any special characters.
64
+ * @param string $text The original string.
65
+ * @return string The string as HTML.
66
+ */
67
+ protected function _esc_html($text){
68
+ return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
69
+ }
70
+
71
+ /**
72
+ * Render a single particular value.
73
+ * @param mixed $var The value to render
74
+ * @param string $class Parent CSS class.
75
+ * @param string $id Item HTML id.
76
+ */
77
+ protected function _generate_value($var, $class = '', $id = ''){
78
+ $BEENTHERE = self::$BEEN_THERE;
79
+ $class .= ' '.$this->css_class.'_t_'.gettype($var);
80
+
81
+ $html = '<div id="'.$id.'" class="'.$class.'">';
82
+
83
+ switch(true){
84
+
85
+ // handle arrays
86
+ case is_array($var):
87
+ if(isset($var[$BEENTHERE])){
88
+ $html .= '<span class="'.$this->css_class.'_ir">Infinite Recursion Detected!</span>';
89
+ }else{
90
+ $var[$BEENTHERE] = true;
91
+ $has_subitems = false;
92
+ foreach($var as $k=>$v){
93
+ if($k!==$BEENTHERE){
94
+ $html .= $this->_generate_keyvalue($k, $v);
95
+ $has_subitems = true;
96
+ }
97
+ }
98
+ if(!$has_subitems){
99
+ $html .= '<span class="'.$this->css_class.'_ni">Empty Array</span>';
100
+ }
101
+ unset($var[$BEENTHERE]);
102
+ }
103
+ break;
104
+
105
+ // handle objects
106
+ case is_object($var):
107
+ if(isset($var->$BEENTHERE)){
108
+ $html .= '<span class="'.$this->css_class.'_ir">Infinite Recursion Detected!</span>';
109
+ }else{
110
+ $var->$BEENTHERE = true;
111
+ $has_subitems = false;
112
+ foreach((array)$var as $k=>$v){
113
+ if($k!==$BEENTHERE){
114
+ $html .= $this->_generate_keyvalue($k, $v);
115
+ $has_subitems = true;
116
+ }
117
+ }
118
+ if(!$has_subitems){
119
+ $html .= '<span class="'.$this->css_class.'_ni">No Properties</span>';
120
+ }
121
+ unset($var->$BEENTHERE);
122
+ }
123
+ break;
124
+
125
+ // handle simple types
126
+ default:
127
+ $html .= $this->_generate_keyvalue('', $var);
128
+ break;
129
+ }
130
+
131
+ return $html . '</div>';
132
+ }
133
+
134
+ /**
135
+ * Render a key-value pair.
136
+ * @staticvar int $id Specifies element id.
137
+ * @param string $key Key name.
138
+ * @param mixed $val Key value.
139
+ */
140
+ protected function _generate_keyvalue($key, $val){
141
+ static $id = 0; $id++; // unique (per rquest) id
142
+ $p = ''; // preview
143
+ $d = ''; // description
144
+ $t = gettype($val); // get data type
145
+ $is_hash = ($t=='array') || ($t=='object');
146
+
147
+ switch($t){
148
+ case 'boolean':
149
+ $p = $val ? 'TRUE' : 'FALSE';
150
+ break;
151
+ case 'integer':
152
+ case 'double':
153
+ $p = (string)$val;
154
+ break;
155
+ case 'string':
156
+ $d .= ', '.strlen($val).' characters';
157
+ $p = $val;
158
+ break;
159
+ case 'resource':
160
+ $d .= ', '.get_resource_type($val).' type';
161
+ $p = (string)$val;
162
+ break;
163
+ case 'array':
164
+ $d .= ', '.count($val).' elements';
165
+ break;
166
+ case 'object':
167
+ $d .= ', '.get_class($val).', '.count(get_object_vars($val)).' properties';
168
+ break;
169
+ }
170
+
171
+ $cls = $this->css_class;
172
+ $xcls = !$is_hash ? $cls.'_ad' : '';
173
+ $html = '<a href="javascript:;" onclick="'.$this->js_func.'(\''.$this->html_id.'\',\''.$id.'\');">';
174
+ $html .= ' <span class="'.$cls.'_a '.$xcls.'" id="'.$this->html_id.'_a'.$id.'">&#9658;</span>';
175
+ $html .= ' <span class="'.$cls.'_k">'.$this->_esc_html($key).'</span>';
176
+ $html .= ' <span class="'.$cls.'_d">(<span>'.ucwords($t).'</span>'.$d.')</span>';
177
+ $html .= ' <span class="'.$cls.'_p '.$cls.'_t_'.$t.'">'.$this->_esc_html($p).'</span>';
178
+ $html .= '</a>';
179
+
180
+ if($is_hash){
181
+ $html .= $this->_generate_value($val, $cls.'_v', $this->html_id.'_v'.$id);
182
+ }
183
+
184
+ return $html;
185
+ }
186
+ }
classes/SensorManager.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ final class WSAL_SensorManager extends WSAL_AbstractSensor {
4
+
5
+ protected $sensors = array();
6
+
7
+ public function __construct(WpSecurityAuditLog $plugin){
8
+ parent::__construct($plugin);
9
+
10
+ foreach(glob(dirname(__FILE__) . '/Sensors/*.php') as $file){
11
+ $class = $plugin->GetClassFileClassName($file);
12
+ $this->sensors[] = new $class($plugin);
13
+ }
14
+ }
15
+
16
+ public function HookEvents() {
17
+ foreach($this->sensors as $sensor)
18
+ $sensor->HookEvents();
19
+ }
20
+
21
+ }
classes/Sensors/Content.php ADDED
@@ -0,0 +1,405 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_Content extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ if(is_admin())add_action('init', array($this, 'EventWordpressInit'));
7
+ add_action('transition_post_status', array($this, 'EventPostChanged'), 10, 3);
8
+ add_action('delete_post', array($this, 'EventPostDeleted'), 10, 1);
9
+ add_action('wp_trash_post', array($this, 'EventPostTrashed'), 10, 1);
10
+ add_action('untrash_post', array($this, 'EventPostUntrashed'));
11
+ }
12
+
13
+ protected function GetEventTypeForPostType($post, $typePost, $typePage, $typeCustom){
14
+ switch($post->post_type){
15
+ case 'page':
16
+ return $typePage;
17
+ case 'post':
18
+ return $typePost;
19
+ default:
20
+ return $typeCustom;
21
+ }
22
+ }
23
+
24
+ protected $_OldPost = null;
25
+ protected $_OldLink = null;
26
+ protected $_OldCats = null;
27
+ protected $_OldTmpl = null;
28
+ protected $_OldStky = null;
29
+
30
+ public function EventWordpressInit(){
31
+ // load old data, if applicable
32
+ $this->RetrieveOldData();
33
+ // check for category changes
34
+ $this->CheckCategoryCreation();
35
+ $this->CheckCategoryDeletion();
36
+ }
37
+
38
+ protected function RetrieveOldData(){
39
+ if (isset($_POST) && isset($_POST['post_ID'])
40
+ && !(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
41
+ && !(isset($_POST['action']) && $_POST['action'] == 'autosave')
42
+ ){
43
+ $postID = intval($_POST['post_ID']);
44
+ $this->_OldPost = get_post($postID);
45
+ $this->_OldLink = get_permalink($postID);
46
+ $this->_OldTmpl = $this->GetPostTemplate($this->_OldPost);
47
+ $this->_OldCats = $this->GetPostCategories($this->_OldPost);
48
+ $this->_OldStky = in_array($postID, get_option('sticky_posts'));
49
+ }
50
+ }
51
+
52
+ protected function GetPostTemplate($post){
53
+ $id = $post->ID;
54
+ $template = get_page_template_slug($id);
55
+ $pagename = $post->post_name;
56
+
57
+ $templates = array();
58
+ if ( $template && 0 === validate_file( $template ) ) $templates[] = $template;
59
+ if ( $pagename ) $templates[] = "page-$pagename.php";
60
+ if ( $id ) $templates[] = "page-$id.php";
61
+ $templates[] = 'page.php';
62
+
63
+ return get_query_template( 'page', $templates );
64
+ }
65
+
66
+ protected function GetPostCategories($post){
67
+ return wp_get_post_categories($post->ID, array('fields' => 'names'));;
68
+ }
69
+
70
+ public function EventPostChanged($newStatus, $oldStatus, $post){
71
+ // ignorable states
72
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
73
+ if (empty($post->post_type)) return;
74
+ if ($post->post_type == 'revision') return;
75
+
76
+ $original = isset($_POST['original_post_status']) ? $_POST['original_post_status'] : '';
77
+
78
+ WSAL_Sensors_Request::SetVars(array(
79
+ '$newStatus' => $newStatus,
80
+ '$oldStatus' => $oldStatus,
81
+ '$original' => $original,
82
+ ));
83
+
84
+ // run checks
85
+ if($this->_OldPost){
86
+ if ($oldStatus == 'auto-draft' || $original == 'auto-draft'){
87
+
88
+ // Handle create post events
89
+ $this->CheckPostCreation($this->_OldPost, $post);
90
+
91
+ }else{
92
+
93
+ // Handle update post events
94
+ $changes = 0
95
+ + $this->CheckDateChange($this->_OldPost, $post)
96
+ + $this->CheckAuthorChange($this->_OldPost, $post)
97
+ + $this->CheckStatusChange($this->_OldPost, $post)
98
+ + $this->CheckParentChange($this->_OldPost, $post)
99
+ + $this->CheckStickyChange($this->_OldStky, isset($_REQUEST['sticky']), $post)
100
+ + $this->CheckVisibilityChange($this->_OldPost, $post, $oldStatus, $newStatus)
101
+ + $this->CheckTemplateChange($this->_OldTmpl, $this->GetPostTemplate($post), $post)
102
+ + $this->CheckCategoriesChange($this->_OldCats, $this->GetPostCategories($post), $post)
103
+ ;
104
+ if(!$changes)
105
+ $changes = $this->CheckPermalinkChange($this->_OldLink, get_permalink($post->ID), $post);
106
+ if(!$changes)
107
+ $changes = $this->CheckModificationChange($this->_OldPost, $post);
108
+
109
+ }
110
+ }
111
+ }
112
+
113
+ protected function CheckPostCreation($oldPost, $newPost){
114
+ $event = 0;
115
+ switch($newPost->post_status){
116
+ case 'publish':
117
+ $event = $this->GetEventTypeForPostType($oldPost, 2001, 2005, 2030);
118
+ break;
119
+ case 'draft':
120
+ $event = $this->GetEventTypeForPostType($oldPost, 2000, 2004, 2029);
121
+ break;
122
+ }
123
+ if($event)$this->plugin->alerts->Trigger($event, array(
124
+ 'PostID' => $newPost->ID,
125
+ 'PostType' => $newPost->post_type,
126
+ 'PostTitle' => $newPost->post_title,
127
+ 'PostUrl' => get_permalink($newPost->ID),
128
+ ));
129
+ }
130
+
131
+ protected function CheckCategoryCreation(){
132
+ if (empty($_POST)) return;
133
+
134
+ $categoryName = '';
135
+ if(!empty($_POST['screen']) && !empty($_POST['tag-name']) &&
136
+ $_POST['screen'] == 'edit-category' &&
137
+ $_POST['taxonomy'] == 'category' &&
138
+ $_POST['action'] == 'add-tag')
139
+ {
140
+ $categoryName = $_POST['tag-name'];
141
+ }
142
+ elseif(!empty($_POST['newcategory']) && $_POST['action'] == 'add-category')
143
+ {
144
+ $categoryName = $_POST['newcategory'];
145
+ }
146
+
147
+ if($categoryName){
148
+ $this->plugin->alerts->Trigger(2023, array(
149
+ 'CategoryName' => $categoryName,
150
+ ));
151
+ }
152
+ }
153
+
154
+ protected function CheckCategoryDeletion(){
155
+ if (empty($_POST)) return;
156
+ $action = !empty($_POST['action']) ? $_POST['action']
157
+ : (!empty($_POST['action2']) ? $_POST['action2'] : '');
158
+ if (!$action) return;
159
+
160
+ $categoryIds = array();
161
+
162
+ if($action == 'delete' && $_POST['taxonomy'] == 'category' && !empty($_POST['delete_tags'])){
163
+ // bulk delete
164
+ $categoryIds[] = $_POST['delete_tags'];
165
+ }elseif($action == 'delete-tag' && $_POST['taxonomy'] == 'category' && !empty($_POST['tag_ID'])){
166
+ // single delete
167
+ $categoryIds[] = $_POST['tag_ID'];
168
+ }
169
+
170
+ foreach($categoryIds as $categoryID){
171
+ $category = get_category($categoryID);
172
+ $this->plugin->alerts->Trigger(2024, array(
173
+ 'CategoryID' => $categoryID,
174
+ 'CategoryName' => $category->cat_name,
175
+ ));
176
+ }
177
+ }
178
+
179
+ public function EventPostDeleted($post_id){
180
+ $post = get_post($post_id);
181
+ if(!in_array($post->post_type, array('attachment', 'revision'))){ // ignore attachments and revisions
182
+ $event = $this->GetEventTypeForPostType($post, 2008, 2009, 2033);
183
+ $this->plugin->alerts->Trigger($event, array(
184
+ 'PostID' => $post->ID,
185
+ 'PostType' => $post->post_type,
186
+ 'PostTitle' => $post->post_title,
187
+ ));
188
+ }
189
+ }
190
+
191
+ public function EventPostTrashed($post_id){
192
+ $post = get_post($post_id);
193
+ $event = $this->GetEventTypeForPostType($post, 2012, 2013, 2034);
194
+ $this->plugin->alerts->Trigger($event, array(
195
+ 'PostID' => $post->ID,
196
+ 'PostType' => $post->post_type,
197
+ 'PostTitle' => $post->post_title,
198
+ ));
199
+ }
200
+
201
+ public function EventPostUntrashed($post_id){
202
+ $post = get_post($post_id);
203
+ $event = $this->GetEventTypeForPostType($post, 2014, 2015, 2035);
204
+ $this->plugin->alerts->Trigger($event, array(
205
+ 'PostID' => $post->ID,
206
+ 'PostType' => $post->post_type,
207
+ 'PostTitle' => $post->post_title,
208
+ ));
209
+ }
210
+
211
+ protected function CheckDateChange($oldpost, $newpost){
212
+ $from = strtotime($oldpost->post_date);
213
+ $to = strtotime($newpost->post_date);
214
+ if($oldpost->post_status == 'draft')return;
215
+ if($from != $to){
216
+ $event = $this->GetEventTypeForPostType($oldpost, 2027, 2028, 2041);
217
+ $this->plugin->alerts->Trigger($event, array(
218
+ 'PostID' => $oldpost->ID,
219
+ 'PostType' => $oldpost->post_type,
220
+ 'PostTitle' => $oldpost->post_title,
221
+ 'OldDate' => $oldpost->post_date,
222
+ 'NewDate' => $newpost->post_date,
223
+ ));
224
+ return 1;
225
+ }
226
+ }
227
+
228
+ protected function CheckCategoriesChange($oldCats, $newCats, $post){
229
+ $oldCats = implode(', ', $oldCats);
230
+ $newCats = implode(', ', $newCats);
231
+ if($oldCats != $newCats){
232
+ $event = $this->GetEventTypeForPostType($post, 2016, 0, 2036);
233
+ if($event){
234
+ $this->plugin->alerts->Trigger($event, array(
235
+ 'PostID' => $post->ID,
236
+ 'PostType' => $post->post_type,
237
+ 'PostTitle' => $post->post_title,
238
+ 'OldCategories' => $oldCats ? $oldCats : 'no categories',
239
+ 'NewCategories' => $newCats ? $newCats : 'no categories',
240
+ ));
241
+ return 1;
242
+ }
243
+ }
244
+ }
245
+
246
+ protected function CheckAuthorChange($oldpost, $newpost){
247
+ if($oldpost->post_author != $newpost->post_author){
248
+ $event = $this->GetEventTypeForPostType($oldpost, 2019, 2020, 2038);
249
+ $this->plugin->alerts->Trigger($event, array(
250
+ 'PostID' => $oldpost->ID,
251
+ 'PostType' => $oldpost->post_type,
252
+ 'PostTitle' => $oldpost->post_title,
253
+ 'OldAuthor' => get_userdata($oldpost->post_author)->user_login,
254
+ 'NewAuthor' => get_userdata($newpost->post_author)->user_login,
255
+ ));
256
+ return 1;
257
+ }
258
+ }
259
+
260
+ protected function CheckStatusChange($oldpost, $newpost){
261
+ if($oldpost->post_status != $newpost->post_status){
262
+ if(isset($_REQUEST['publish'])){
263
+ // special case (publishing a post)
264
+ $event = $this->GetEventTypeForPostType($oldpost, 2001, 2005, 2030);
265
+ $this->plugin->alerts->Trigger($event, array(
266
+ 'PostID' => $newpost->ID,
267
+ 'PostType' => $newpost->post_type,
268
+ 'PostTitle' => $newpost->post_title,
269
+ 'PostUrl' => get_permalink($newpost->ID),
270
+ ));
271
+ }else{
272
+ $event = $this->GetEventTypeForPostType($oldpost, 2021, 2022, 2039);
273
+ $this->plugin->alerts->Trigger($event, array(
274
+ 'PostID' => $oldpost->ID,
275
+ 'PostType' => $oldpost->post_type,
276
+ 'PostTitle' => $oldpost->post_title,
277
+ 'OldStatus' => $oldpost->post_status,
278
+ 'NewStatus' => $newpost->post_status,
279
+ ));
280
+ }
281
+ return 1;
282
+ }
283
+ }
284
+
285
+ protected function CheckParentChange($oldpost, $newpost){
286
+ if($oldpost->post_parent != $newpost->post_parent){
287
+ $event = $this->GetEventTypeForPostType($oldpost, 0, 2047, 0);
288
+ if($event){
289
+ $this->plugin->alerts->Trigger($event, array(
290
+ 'PostID' => $oldpost->ID,
291
+ 'PostType' => $oldpost->post_type,
292
+ 'PostTitle' => $oldpost->post_title,
293
+ 'OldParent' => $oldpost->post_parent,
294
+ 'NewParent' => $newpost->post_parent,
295
+ 'OldParentName' => $oldpost->post_parent ? get_the_title($oldpost->post_parent) : 'no parent',
296
+ 'NewParentName' => $newpost->post_parent ? get_the_title($newpost->post_parent) : 'no parent',
297
+ ));
298
+ return 1;
299
+ }
300
+ }
301
+ }
302
+
303
+ protected function CheckPermalinkChange($oldLink, $newLink, $post){
304
+ if($oldLink != $newLink){
305
+ $event = $this->GetEventTypeForPostType($post, 2017, 2018, 2037);
306
+ $this->plugin->alerts->Trigger($event, array(
307
+ 'PostID' => $post->ID,
308
+ 'PostType' => $post->post_type,
309
+ 'PostTitle' => $post->post_title,
310
+ 'OldUrl' => $oldLink,
311
+ 'NewUrl' => $newLink,
312
+ ));
313
+ return 1;
314
+ }
315
+ }
316
+
317
+ protected function CheckVisibilityChange($oldpost, $newpost, $oldStatus, $newStatus){
318
+ if($oldStatus == 'draft' || $newStatus == 'draft')return;
319
+
320
+ $oldVisibility = '';
321
+ $newVisibility = '';
322
+
323
+ if($oldpost->post_password){
324
+ $oldVisibility = __('Password Protected');
325
+ }elseif($oldStatus == 'publish'){
326
+ $oldVisibility = __('Public');
327
+ }elseif($oldStatus == 'private'){
328
+ $oldVisibility = __('Private');
329
+ }
330
+
331
+ if($newpost->post_password){
332
+ $newVisibility = __('Password Protected');
333
+ }elseif($newStatus == 'publish'){
334
+ $newVisibility = __('Public');
335
+ }elseif($newStatus == 'private'){
336
+ $newVisibility = __('Private');
337
+ }
338
+
339
+ if($oldVisibility && $newVisibility && ($oldVisibility != $newVisibility)){
340
+ $event = $this->GetEventTypeForPostType($oldpost, 2025, 2026, 2040);
341
+ $this->plugin->alerts->Trigger($event, array(
342
+ 'PostID' => $oldpost->ID,
343
+ 'PostType' => $oldpost->post_type,
344
+ 'PostTitle' => $oldpost->post_title,
345
+ 'OldVisibility' => $oldVisibility,
346
+ 'NewVisibility' => $newVisibility,
347
+ ));
348
+ return 1;
349
+ }
350
+ }
351
+
352
+ protected function CheckTemplateChange($oldTmpl, $newTmpl, $post){
353
+ if($oldTmpl != $newTmpl){
354
+ $event = $this->GetEventTypeForPostType($post, 0, 2048, 0);
355
+ if($event){
356
+ $this->plugin->alerts->Trigger($event, array(
357
+ 'PostID' => $post->ID,
358
+ 'PostType' => $post->post_type,
359
+ 'PostTitle' => $post->post_title,
360
+ 'OldTemplate' => ucwords(str_replace(array('-' , '_'), ' ', basename($oldTmpl, '.php'))),
361
+ 'NewTemplate' => ucwords(str_replace(array('-' , '_'), ' ', basename($newTmpl, '.php'))),
362
+ 'OldTemplatePath' => $oldTmpl,
363
+ 'NewTemplatePath' => $newTmpl,
364
+ ));
365
+ return 1;
366
+ }
367
+ }
368
+ }
369
+
370
+ protected function CheckStickyChange($oldStky, $newStky, $post){
371
+ if($oldStky != $newStky){
372
+ $event = $newStky ? 2049 : 2050;
373
+ $this->plugin->alerts->Trigger($event, array(
374
+ 'PostID' => $post->ID,
375
+ 'PostType' => $post->post_type,
376
+ 'PostTitle' => $post->post_title,
377
+ ));
378
+ return 1;
379
+ }
380
+ }
381
+
382
+ protected function CheckModificationChange($oldpost, $newpost){
383
+ if($oldpost->post_modified != $newpost->post_modified){
384
+ $event = 0;
385
+ // @see http://codex.wordpress.org/Class_Reference/WP_Query#Status_Parameters
386
+ switch($oldpost->post_status){ // TODO or should this be $newpost?
387
+ case 'draft':
388
+ $event = $this->GetEventTypeForPostType($newpost, 2003, 2007, 2032);
389
+ break;
390
+ case 'publish':
391
+ $event = $this->GetEventTypeForPostType($newpost, 2002, 2006, 2031);
392
+ break;
393
+ }
394
+ if($event){
395
+ $this->plugin->alerts->Trigger($event, array(
396
+ 'PostID' => $oldpost->ID,
397
+ 'PostType' => $oldpost->post_type,
398
+ 'PostTitle' => $oldpost->post_title,
399
+ 'PostUrl' => get_permalink($oldpost->ID), // TODO or should this be $newpost?
400
+ ));
401
+ return 1;
402
+ }
403
+ }
404
+ }
405
+ }
classes/Sensors/Files.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_Files extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ add_action('add_attachment', array($this, 'EventFileUploaded'));
7
+ add_action('delete_attachment', array($this, 'EventFileUploadedDeleted'));
8
+ add_action('admin_init', array($this, 'EventAdminInit'));
9
+ }
10
+
11
+ protected $IsFileUploaded = false;
12
+
13
+ public function EventFileUploaded($attachmentID){
14
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
15
+ if($action != 'upload-theme' && $action != 'upload-plugin'){
16
+ $file = get_attached_file($attachmentID);
17
+ $this->plugin->alerts->Trigger(2010, array(
18
+ 'AttachmentID' => $attachmentID,
19
+ 'FileName' => basename($file),
20
+ 'FilePath' => dirname($file),
21
+ ));
22
+ }
23
+ $this->IsFileUploaded = true;
24
+ }
25
+
26
+ public function EventFileUploadedDeleted($attachmentID){
27
+ if($this->IsFileUploaded)return;
28
+ $file = get_attached_file($attachmentID);
29
+ $this->plugin->alerts->Trigger(2011, array(
30
+ 'AttachmentID' => $attachmentID,
31
+ 'FileName' => basename($file),
32
+ 'FilePath' => dirname($file),
33
+ ));
34
+ }
35
+
36
+ public function EventAdminInit(){
37
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
38
+ $is_theme_editor = basename($_SERVER['SCRIPT_NAME']) == 'theme-editor.php';
39
+ if($is_theme_editor && $action == 'update'){
40
+ $this->plugin->alerts->Trigger(2046, array(
41
+ 'File' => $_REQUEST['file'],
42
+ 'Theme' => $_REQUEST['theme'],
43
+ ));
44
+ }
45
+ }
46
+
47
+ }
classes/Sensors/LogInOut.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ add_action('wp_login', array($this, 'EventLogin'), 10, 2);
7
+ add_action('wp_logout', array($this, 'EventLogout'));
8
+ add_action('wp_login_failed', array($this, 'EventLoginFailure'));
9
+ }
10
+
11
+ public function EventLogin($user_login, $user){
12
+ $this->plugin->alerts->Trigger(1000, array('Username' => $user_login));
13
+ }
14
+
15
+ public function EventLogout(){
16
+ $this->plugin->alerts->Trigger(1001);
17
+ }
18
+
19
+ public function EventLoginFailure($username){
20
+ list($y, $m, $d) = explode('-', date('Y-m-d'));
21
+ $occ = WSAL_DB_Occurrence::LoadMultiQuery('
22
+ SELECT * FROM `wp_wsal_occurrences`
23
+ WHERE alert_id = %d AND site_id = %d
24
+ AND (created_on BETWEEN %d AND %d)
25
+ AND id IN (
26
+ SELECT occurrence_id as id
27
+ FROM wp_wsal_metadata
28
+ WHERE (name = "ClientIP" AND value = %s)
29
+ OR (name = "Username" AND value = %s)
30
+ GROUP BY occurrence_id
31
+ HAVING COUNT(*) = 2
32
+ )
33
+ ', array(
34
+ 1002,
35
+ (function_exists('get_current_blog_id') ? get_current_blog_id() : 0),
36
+ mktime(0, 0, 0, $m, $d, $y),
37
+ mktime(0, 0, 0, $m, $d + 1, $y) - 1,
38
+ json_encode(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''),
39
+ json_encode($username),
40
+ ));
41
+ $occ = count($occ) ? $occ[0] : null;
42
+
43
+ if($occ && $occ->IsLoaded()){
44
+ // update existing record
45
+ $occ->SetMetaValue('Attempts',
46
+ $occ->GetMetaValue('Attempts', 0) + 1
47
+ );
48
+ $occ->created_on = current_time('timestamp');
49
+ $occ->Save();
50
+ }else{
51
+ // create a new record
52
+ $this->plugin->alerts->Trigger(1002, array(
53
+ 'Username' => $username,
54
+ 'Attempts' => 1
55
+ ));
56
+ }
57
+ }
58
+
59
+ }
classes/Sensors/Multisite.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_Multisite extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ add_action('wpmu_new_blog', array($this, 'EventNewBlog'), 10, 1);
7
+ add_action('archive_blog', array($this, 'EventArchiveBlog'));
8
+ add_action('unarchive_blog', array($this, 'EventUnarchiveBlog'));
9
+ add_action('activate_blog', array($this, 'EventActivateBlog'));
10
+ add_action('deactivate_blog', array($this, 'EventDeactivateBlog'));
11
+ add_action('delete_blog', array($this, 'EventDeleteBlog'));
12
+ add_action('add_user_to_blog', array($this, 'EventUserAddedToBlog'), 10, 3);
13
+ add_action('remove_user_from_blog', array($this, 'EventUserRemovedFromBlog'));
14
+ }
15
+
16
+ public function EventNewBlog($blog_id){
17
+ $this->plugin->alerts->Trigger(7000, array(
18
+ 'BlogID' => $blog_id,
19
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
20
+ ));
21
+ }
22
+
23
+ public function EventArchiveBlog($blog_id){
24
+ $this->plugin->alerts->Trigger(7001, array(
25
+ 'BlogID' => $blog_id,
26
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
27
+ ));
28
+ }
29
+
30
+ public function EventUnarchiveBlog($blog_id){
31
+ $this->plugin->alerts->Trigger(7002, array(
32
+ 'BlogID' => $blog_id,
33
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
34
+ ));
35
+ }
36
+
37
+ public function EventActivateBlog($blog_id){
38
+ $this->plugin->alerts->Trigger(7003, array(
39
+ 'BlogID' => $blog_id,
40
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
41
+ ));
42
+ }
43
+
44
+ public function EventDeactivateBlog($blog_id){
45
+ $this->plugin->alerts->Trigger(7004, array(
46
+ 'BlogID' => $blog_id,
47
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
48
+ ));
49
+ }
50
+
51
+ public function EventDeleteBlog($blog_id){
52
+ $this->plugin->alerts->Trigger(7005, array(
53
+ 'BlogID' => $blog_id,
54
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
55
+ ));
56
+ }
57
+
58
+ public function EventUserAddedToBlog($user_id, $role, $blog_id){
59
+ $this->plugin->alerts->Trigger(4010, array(
60
+ 'UserID' => $user_id,
61
+ 'Username' => get_userdata($user_id)->user_login,
62
+ 'UserRole' => $role,
63
+ 'BlogID' => $blog_id,
64
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
65
+ ));
66
+ }
67
+
68
+ public function EventUserRemovedFromBlog($user_id){
69
+ $user = get_userdata($user_id);
70
+ $blog_id = (isset($_REQUEST['id']) ? $_REQUEST['id'] : 0);
71
+ $this->plugin->alerts->Trigger(4011, array(
72
+ 'UserID' => $user_id,
73
+ 'Username' => $user->user_login,
74
+ 'UserRole' => is_array($user->roles) ? implode(', ', $user->roles) : $user->roles,
75
+ 'BlogID' => $blog_id,
76
+ 'SiteName' => get_blog_option($blog_id, 'blogname'),
77
+ ));
78
+ }
79
+ }
classes/Sensors/PhpErrors.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_PhpErrors extends WSAL_AbstractSensor {
4
+
5
+ protected $_avoid_error_recursion = false;
6
+
7
+ protected $_error_types = array(
8
+ 0001 => array(1,4,16,64,256,4096), // errors
9
+ 0002 => array(2,32,128,512), // warnings
10
+ 0003 => array(8,1024,2048,8192,16384), // notices
11
+ );
12
+
13
+ protected $_maybe_last_error = null;
14
+
15
+ public function HookEvents() {
16
+ if($this->plugin->settings->IsPhpErrorLoggingEnabled()){
17
+ set_error_handler(array($this, 'EventError'), E_ALL);
18
+ set_exception_handler(array($this, 'EventException'));
19
+ register_shutdown_function(array($this, 'EventShutdown'));
20
+ }
21
+ }
22
+
23
+ protected function GetErrorHash($code, $mesg, $file, $line){
24
+ return md5(implode(':', func_get_args()));
25
+ }
26
+
27
+ public function EventError($errno, $errstr, $errfile = 'unknown', $errline = 0, $errcontext = array()){
28
+ if($this->_avoid_error_recursion)return;
29
+
30
+ $data = array(
31
+ 'Code' => $errno,
32
+ 'Message' => $errstr,
33
+ 'File' => $errfile,
34
+ 'Line' => $errline,
35
+ 'Context' => $errcontext,
36
+ );
37
+
38
+ $type = 0002; // default (middle ground)
39
+ foreach($this->_error_types as $temp => $codes){
40
+ if(in_array($errno, $codes)){
41
+ $type = $temp;
42
+ }
43
+ }
44
+
45
+ $this->_maybe_last_error = $this->GetErrorHash($errno, $errstr, $errfile, $errline);
46
+
47
+ $this->_avoid_error_recursion = true;
48
+ $this->plugin->alerts->Trigger($type, $data);
49
+ $this->_avoid_error_recursion = false;
50
+ }
51
+
52
+ public function EventException(Exception $ex){
53
+ if($this->_avoid_error_recursion)return;
54
+
55
+ $data = array(
56
+ 'Class' => get_class($ex),
57
+ 'Code' => $ex->getCode(),
58
+ 'Message' => $ex->getMessage(),
59
+ 'File' => $ex->getFile(),
60
+ 'Line' => $ex->getLine(),
61
+ 'Trace' => $ex->getTraceAsString(),
62
+ );
63
+
64
+ if(method_exists($ex, 'getContext'))
65
+ $data['Context'] = $ex->getContext();
66
+
67
+ $this->_avoid_error_recursion = true;
68
+ $this->plugin->alerts->Trigger(0004, $data);
69
+ $this->_avoid_error_recursion = false;
70
+ }
71
+
72
+ public function EventShutdown(){
73
+ if($this->_avoid_error_recursion)return;
74
+
75
+ if(!!($e = error_get_last()) && ($this->_maybe_last_error != $this->GetErrorHash($e['type'], $e['message'], $e['file'], $e['line']))){
76
+ $data = array(
77
+ 'Code' => $e['type'],
78
+ 'Message' => $e['message'],
79
+ 'File' => $e['file'],
80
+ 'Line' => $e['line'],
81
+ );
82
+
83
+ $this->_avoid_error_recursion = true;
84
+ $this->plugin->alerts->Trigger(0005, $data);
85
+ $this->_avoid_error_recursion = false;
86
+ }
87
+ }
88
+
89
+ }
classes/Sensors/PluginsThemes.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_PluginsThemes extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ add_action('admin_init', array($this, 'EventAdminInit'));
7
+ if(is_admin())add_action('shutdown', array($this, 'EventAdminShutdown'));
8
+ add_action('switch_theme', array($this, 'EventThemeActivated'));
9
+ }
10
+
11
+ protected $old_themes;
12
+ protected $old_plugins;
13
+
14
+ public function EventAdminInit(){
15
+ $this->old_themes = wp_get_themes();
16
+ $this->old_plugins = get_plugins();
17
+ }
18
+
19
+ public function EventAdminShutdown(){
20
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
21
+ $action = isset($_REQUEST['action2']) ? $_REQUEST['action2'] : $action;
22
+ $actype = basename($_SERVER['SCRIPT_NAME'], '.php');
23
+ $is_themes = $actype == 'themes';
24
+ $is_plugins = $actype == 'plugins';
25
+
26
+ // install plugin
27
+ if(($action=='install-plugin' || $action=='upload-plugin')){
28
+ $newPlugin = array_values(array_diff(array_keys(get_plugins()), array_keys($this->old_plugins)));
29
+ if(count($newPlugin) != 1)
30
+ return $this->LogError(
31
+ 'Expected exactly one new plugin but found ' . count($newPlugin),
32
+ array('NewPlugin' => $newPlugin, 'OldPlugins' => $this->old_plugins, 'NewPlugins' => get_plugins())
33
+ );
34
+ $newPluginPath = $newPlugin[0];
35
+ $newPlugin = get_plugins();
36
+ $newPlugin = $newPlugin[$newPluginPath];
37
+ $newPluginPath = plugin_dir_path(WP_PLUGIN_DIR . '/' . $newPluginPath[0]);
38
+ $this->plugin->alerts->Trigger(5000, array(
39
+ 'NewPlugin' => (object)array(
40
+ 'Name' => $newPlugin['Name'],
41
+ 'PluginURI' => $newPlugin['PluginURI'],
42
+ 'Version' => $newPlugin['Version'],
43
+ 'Author' => $newPlugin['Author'],
44
+ 'Network' => $newPlugin['Network'] ? 'True' : 'False',
45
+ 'plugin_dir_path' => $newPluginPath,
46
+ ),
47
+ ));
48
+ }
49
+
50
+ // activate plugin
51
+ if($is_plugins && in_array($action, array('activate', 'activate-selected'))){
52
+ if(isset($_REQUEST['plugin'])){
53
+ if(!isset($_REQUEST['checked']))
54
+ $_REQUEST['checked'] = array();
55
+ $_REQUEST['checked'][] = $_REQUEST['plugin'];
56
+ }
57
+ foreach($_REQUEST['checked'] as $pluginFile){
58
+ $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
59
+ $pluginData = get_plugin_data($pluginFile, false, true);
60
+ $this->plugin->alerts->Trigger(5001, array(
61
+ 'PluginFile' => $pluginFile,
62
+ 'PluginData' => (object)array(
63
+ 'Name' => $pluginData['Name'],
64
+ 'PluginURI' => $pluginData['PluginURI'],
65
+ 'Version' => $pluginData['Version'],
66
+ 'Author' => $pluginData['Author'],
67
+ 'Network' => $pluginData['Network'] ? 'True' : 'False',
68
+ ),
69
+ ));
70
+ }
71
+ }
72
+
73
+ // deactivate plugin
74
+ if($is_plugins && in_array($action, array('deactivate', 'deactivate-selected'))){
75
+ if(isset($_REQUEST['plugin'])){
76
+ if(!isset($_REQUEST['checked']))
77
+ $_REQUEST['checked'] = array();
78
+ $_REQUEST['checked'][] = $_REQUEST['plugin'];
79
+ }
80
+ foreach($_REQUEST['checked'] as $pluginFile){
81
+ $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
82
+ $pluginData = get_plugin_data($pluginFile, false, true);
83
+ $this->plugin->alerts->Trigger(5002, array(
84
+ 'PluginFile' => $pluginFile,
85
+ 'PluginData' => (object)array(
86
+ 'Name' => $pluginData['Name'],
87
+ 'PluginURI' => $pluginData['PluginURI'],
88
+ 'Version' => $pluginData['Version'],
89
+ 'Author' => $pluginData['Author'],
90
+ 'Network' => $pluginData['Network'] ? 'True' : 'False',
91
+ ),
92
+ ));
93
+ }
94
+ }
95
+
96
+ // uninstall plugin
97
+ if($is_plugins && in_array($action, array('delete-selected'))){
98
+ if(!isset($_REQUEST['verify-delete'])){
99
+
100
+ // first step, before user approves deletion
101
+ // TODO store plugin data in session here
102
+ }else{
103
+ // second step, after deletion approval
104
+ // TODO use plugin data from session
105
+ foreach($_REQUEST['checked'] as $pluginFile){
106
+ $pluginName = basename($pluginFile, '.php');
107
+ $pluginName = str_replace(array('_', '-', ' '), ' ', $pluginName);
108
+ $pluginName = ucwords($pluginName);
109
+ $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
110
+ $this->plugin->alerts->Trigger(5003, array(
111
+ 'PluginFile' => $pluginFile,
112
+ 'PluginData' => (object)array(
113
+ 'Name' => $pluginName,
114
+ ),
115
+ ));
116
+ }
117
+
118
+ }
119
+ }
120
+
121
+ // upgrade plugin
122
+ if(in_array($action, array('upgrade-plugin', 'update-selected'))){
123
+ if(isset($_REQUEST['plugin'])){
124
+ if(!isset($_REQUEST['checked']))
125
+ $_REQUEST['checked'] = array();
126
+ $_REQUEST['checked'][] = $_REQUEST['plugin'];
127
+ }
128
+ if(isset($_REQUEST['checked'])){
129
+ if(!is_array($_REQUEST['checked'])){
130
+ $_REQUEST['checked'] = array($_REQUEST['checked']);
131
+ }
132
+ foreach($_REQUEST['checked'] as $pluginFile){
133
+ $pluginFile = WP_PLUGIN_DIR . '/' . $pluginFile;
134
+ $pluginData = get_plugin_data($pluginFile, false, true);
135
+ $this->plugin->alerts->Trigger(5004, array(
136
+ 'PluginFile' => $pluginFile,
137
+ 'PluginData' => (object)array(
138
+ 'Name' => $pluginData['Name'],
139
+ 'PluginURI' => $pluginData['PluginURI'],
140
+ 'Version' => $pluginData['Version'],
141
+ 'Author' => $pluginData['Author'],
142
+ 'Network' => $pluginData['Network'] ? 'True' : 'False',
143
+ ),
144
+ ));
145
+ }
146
+ }
147
+ }
148
+
149
+ // install theme
150
+ if($action=='install-theme' || $action=='upload-theme'){
151
+ $newTheme = array_diff(wp_get_themes(), $this->old_themes);
152
+ if(count($newTheme) != 1)
153
+ return $this->LogError(
154
+ 'Expected exactly one new theme but found ' . count($newTheme),
155
+ array('OldThemes' => $this->old_themes, 'NewThemes' => wp_get_themes())
156
+ );
157
+ $newTheme = array_shift($newTheme);
158
+ $this->plugin->alerts->Trigger(5005, array(
159
+ 'NewTheme' => (object)array(
160
+ 'Name' => $newTheme->Name,
161
+ 'ThemeURI' => $newTheme->ThemeURI,
162
+ 'Description' => $newTheme->Description,
163
+ 'Author' => $newTheme->Author,
164
+ 'Version' => $newTheme->Version,
165
+ 'get_template_directory' => $newTheme->get_template_directory(),
166
+ ),
167
+ ));
168
+ }
169
+
170
+ // uninstall theme
171
+ if($is_themes && in_array($action, array('delete-selected'))){
172
+ if(!isset($_REQUEST['verify-delete'])){
173
+
174
+ // first step, before user approves deletion
175
+ // TODO store plugin data in session here
176
+ }else{
177
+ // second step, after deletion approval
178
+ // TODO use plugin data from session
179
+ /*foreach($_REQUEST['checked'] as $themeFile){
180
+
181
+ }*/
182
+
183
+ }
184
+ }
185
+ }
186
+
187
+ public function EventThemeActivated($themeName){
188
+ $newTheme = null;
189
+ foreach(wp_get_themes() as $theme){
190
+ if($theme->Name == $themeName){
191
+ $newTheme = $theme;
192
+ break;
193
+ }
194
+ }
195
+ if($newTheme == null)
196
+ return $this->LogError(
197
+ 'Could not locate theme named "'.$newTheme.'".',
198
+ array('ThemeName' => $themeName, 'Themes' => wp_get_themes())
199
+ );
200
+ $this->plugin->alerts->Trigger(5006, array(
201
+ 'NewTheme' => (object)array(
202
+ 'Name' => $newTheme->Name,
203
+ 'ThemeURI' => $newTheme->ThemeURI,
204
+ 'Description' => $newTheme->Description,
205
+ 'Author' => $newTheme->Author,
206
+ 'Version' => $newTheme->Version,
207
+ 'get_template_directory' => $newTheme->get_template_directory(),
208
+ ),
209
+ ));
210
+ }
211
+
212
+ }
classes/Sensors/Request.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_Request extends WSAL_AbstractSensor {
4
+ public function HookEvents() {
5
+ if($this->plugin->settings->IsRequestLoggingEnabled()){
6
+ add_action('shutdown', array($this, 'EventShutdown'));
7
+ }
8
+ }
9
+
10
+ public function EventShutdown(){
11
+ $file = $this->plugin->GetBaseDir() . 'Request.log.php';
12
+
13
+ $line = '['.date('Y-m-d H:i:s').'] '
14
+ . $_SERVER['REQUEST_METHOD'] . ' '
15
+ . $_SERVER['REQUEST_URI'] . ' '
16
+ . (!empty($_POST) ? str_pad(PHP_EOL, 24) . json_encode($_POST) : '')
17
+ . (!empty(self::$envvars) ? str_pad(PHP_EOL, 24) . json_encode(self::$envvars) : '')
18
+ . PHP_EOL;
19
+
20
+ if(!file_exists($file))
21
+ if(!file_put_contents($file, '<'.'?php die(\'Access Denied\');' . PHP_EOL))
22
+ return $this->LogError('Could not initialize request log file', array('file' => $file));
23
+
24
+ $f = fopen($file, 'a');
25
+ if($f){
26
+ if(!fwrite($f, $line))
27
+ $this->LogWarn('Could not write to log file', array('file' => $file));
28
+ if(!fclose($f))
29
+ $this->LogWarn('Could not close log file', array('file' => $file));
30
+ }else $this->LogWarn('Could not open log file', array('file' => $file));
31
+ }
32
+
33
+ protected static $envvars = array();
34
+
35
+ public static function SetVar($name, $value){
36
+ self::$envvars[$name] = $value;
37
+ }
38
+
39
+ public static function SetVars($data){
40
+ foreach($data as $name => $value)self::SetVar($name, $value);
41
+ }
42
+ }
classes/Sensors/System.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_System extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ add_action('wsal_prune', array($this, 'EventPruneEvents'), 10, 2);
7
+ add_action('admin_init', array($this, 'EventAdminInit'));
8
+ }
9
+
10
+ /**
11
+ * @param WSAL_DB_Occurrence[] $events The events that were deleted.
12
+ */
13
+ public function EventPruneEvents($events, $query){
14
+ $this->plugin->alerts->Trigger(6000, array(
15
+ 'EventCount' => count($events),
16
+ 'PruneQuery' => $query,
17
+ ));
18
+ }
19
+
20
+ public function EventAdminInit(){
21
+
22
+ // make sure user can actually modify target options
23
+ if(!current_user_can('manage_options'))return;
24
+
25
+ $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
26
+ $actype = basename($_SERVER['SCRIPT_NAME'], '.php');
27
+ $is_option_page = $actype == 'options';
28
+ $is_network_settings = $actype == 'settings';
29
+ $is_permalink_page = $actype == 'options-permalink';
30
+
31
+ if($is_option_page && (get_option('users_can_register') xor isset($_POST['users_can_register']))){
32
+ $old = get_option('users_can_register') ? 'Enabled' : 'Disabled';
33
+ $new = isset($_POST['users_can_register']) ? 'Enabled' : 'Disabled';
34
+ if($old !== $new){
35
+ $this->plugin->alerts->Trigger(6001, array(
36
+ 'OldValue' => $old,
37
+ 'NewValue' => $new,
38
+ 'CurrentUserID' => wp_get_current_user()->ID,
39
+ ));
40
+ }
41
+ }
42
+
43
+ if($is_option_page && !empty($_POST['default_role'])){
44
+ $old = get_option('default_role');
45
+ $new = trim($_POST['default_role']);
46
+ if($old !== $new){
47
+ $this->plugin->alerts->Trigger(6002, array(
48
+ 'OldRole' => $old,
49
+ 'NewRole' => $new,
50
+ 'CurrentUserID' => wp_get_current_user()->ID,
51
+ ));
52
+ }
53
+ }
54
+
55
+ if($is_option_page && !empty($_POST['admin_email'])){
56
+ $old = get_option('admin_email');
57
+ $new = trim($_POST['admin_email']);
58
+ if($old !== $new){
59
+ $this->plugin->alerts->Trigger(6003, array(
60
+ 'OldEmail' => $old,
61
+ 'NewEmail' => $new,
62
+ 'CurrentUserID' => wp_get_current_user()->ID,
63
+ ));
64
+ }
65
+ }
66
+
67
+ if($is_network_settings && !empty($_POST['admin_email'])){
68
+ $old = get_site_option('admin_email');
69
+ $new = trim($_POST['admin_email']);
70
+ if($old !== $new){
71
+ $this->plugin->alerts->Trigger(6003, array(
72
+ 'OldEmail' => $old,
73
+ 'NewEmail' => $new,
74
+ 'CurrentUserID' => wp_get_current_user()->ID,
75
+ ));
76
+ }
77
+ }
78
+
79
+ if($is_permalink_page && !empty($_POST['permalink_structure'])){
80
+ $old = get_option('permalink_structure');
81
+ $new = trim($_POST['permalink_structure']);
82
+ if($old !== $new){
83
+ $this->plugin->alerts->Trigger(6005, array(
84
+ 'OldPattern' => $old,
85
+ 'NewPattern' => $new,
86
+ 'CurrentUserID' => wp_get_current_user()->ID,
87
+ ));
88
+ }
89
+ }
90
+
91
+ if($action == 'do-core-upgrade' && isset($_REQUEST['version'])){
92
+ $oldVersion = get_bloginfo('version');
93
+ $newVersion = $_REQUEST['version'];
94
+ if($oldVersion !== $newVersion){
95
+ $this->plugin->alerts->Trigger(6004, array(
96
+ 'OldVersion' => $oldVersion,
97
+ 'NewVersion' => $newVersion,
98
+ ));
99
+ }
100
+ }
101
+ }
102
+
103
+ }
classes/Sensors/UserProfile.php ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_UserProfile extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ add_action('admin_init', array($this, 'EventAdminInit'));
7
+ add_action('user_register', array($this, 'EventUserRegister'));
8
+ add_action('edit_user_profile_update', array($this, 'EventUserChanged'));
9
+ add_action('personal_options_update', array($this, 'EventUserChanged'));
10
+ add_action('delete_user', array($this, 'EventUserDeleted'));
11
+ add_action('wpmu_delete_user', array($this, 'EventUserDeleted'));
12
+ add_action('set_user_role', array($this, 'EventUserRoleChanged'), 10, 3);
13
+ }
14
+
15
+ protected $old_superadmins;
16
+
17
+ protected function IsMultisite(){
18
+ return function_exists('is_multisite') && is_multisite();
19
+ }
20
+
21
+ public function EventAdminInit(){
22
+ if($this->IsMultisite()){
23
+ $this->old_superadmins = get_super_admins();
24
+ }
25
+ }
26
+
27
+ public function EventUserRegister($user_id){
28
+ $user = get_userdata($user_id);
29
+ $ismu = function_exists('is_multisite') && is_multisite();
30
+ $event = $ismu ? 4012 : (is_user_logged_in() ? 4001 : 4000);
31
+ $this->plugin->alerts->Trigger($event, array(
32
+ 'NewUserID' => $user_id,
33
+ 'NewUserData' => (object)array(
34
+ 'Username' => $user->user_login,
35
+ 'FirstName' => $user->user_firstname,
36
+ 'LastName' => $user->user_lastname,
37
+ 'Email' => $user->user_email,
38
+ 'Roles' => is_array($user->roles) ? implode(', ', $user->roles) : $user->roles,
39
+ ),
40
+ ), true);
41
+ }
42
+
43
+ public function EventUserRoleChanged($user_id, $role, $oldRoles){
44
+ $user = get_userdata($user_id);
45
+
46
+ $oldRole = count($oldRoles) ? implode(', ', $oldRoles) : '';
47
+ $newRole = $role;
48
+ if($oldRole != $newRole){
49
+ $this->plugin->alerts->Trigger(4002, array(
50
+ 'TargetUserID' => $user_id,
51
+ 'TargetUsername' => $user->user_login,
52
+ 'OldRole' => $oldRole,
53
+ 'NewRole' => $newRole,
54
+ ));
55
+ }
56
+ }
57
+
58
+ public function EventUserChanged($user_id){
59
+ $user = get_userdata($user_id);
60
+
61
+ // roles changed
62
+ /*if(!empty($_REQUEST['role'])){
63
+ $oldRole = count($user->roles) ? $user->roles[0] : '';
64
+ $newRole = trim($_REQUEST['role']);
65
+ if($oldRole != $newRole){
66
+ $this->plugin->alerts->Trigger(4002, array(
67
+ 'TargetUserID' => $user_id,
68
+ 'TargetUsername' => $user->user_login,
69
+ 'OldRole' => $oldRole,
70
+ 'NewRole' => $newRole,
71
+ ), true);
72
+ }
73
+ }*/
74
+
75
+ // password changed
76
+ if(!empty($_REQUEST['pass1'])){
77
+ $event = $user_id == get_current_user_id() ? 4003 : 4004;
78
+ $this->plugin->alerts->Trigger($event, array(
79
+ 'TargetUserID' => $user_id,
80
+ 'TargetUserData' => (object)array(
81
+ 'Username' => $user->user_login,
82
+ 'Roles' => is_array($user->roles) ? implode(', ', $user->roles) : $user->roles,
83
+ ),
84
+ ));
85
+ }
86
+
87
+ // email changed
88
+ if(!empty($_REQUEST['email'])){
89
+ $oldEmail = $user->user_email;
90
+ $newEmail = trim($_REQUEST['email']);
91
+ if($oldEmail != $newEmail){
92
+ $event = $user_id == get_current_user_id() ? 4005 : 4006;
93
+ $this->plugin->alerts->Trigger($event, array(
94
+ 'TargetUserID' => $user_id,
95
+ 'TargetUsername' => $user->user_login,
96
+ 'OldEmail' => $oldEmail,
97
+ 'NewEmail' => $newEmail,
98
+ ));
99
+ }
100
+ }
101
+
102
+ if($this->IsMultisite()){
103
+ $username = $user->user_login;
104
+ $enabled = isset($_REQUEST['super_admin']);
105
+
106
+ if($user_id != get_current_user_id()){
107
+
108
+ // super admin enabled
109
+ if($enabled && !in_array($username, $this->old_superadmins)){
110
+ $this->plugin->alerts->Trigger(4008, array(
111
+ 'TargetUserID' => $user_id,
112
+ 'TargetUsername' => $user->user_login,
113
+ ));
114
+ }
115
+
116
+ // super admin disabled
117
+ if(!$enabled && in_array($username, $this->old_superadmins)){
118
+ $this->plugin->alerts->Trigger(4009, array(
119
+ 'TargetUserID' => $user_id,
120
+ 'TargetUsername' => $user->user_login,
121
+ ));
122
+ }
123
+
124
+ }
125
+ }
126
+ }
127
+
128
+ public function EventUserDeleted($user_id){
129
+ $user = get_userdata($user_id);
130
+ $role = is_array($user->roles) ? implode(', ', $user->roles) : $user->roles;
131
+ $this->plugin->alerts->TriggerIf(4007, array(
132
+ 'TargetUserID' => $user_id,
133
+ 'TargetUserData' => (object)array(
134
+ 'Username' => $user->user_login,
135
+ 'FirstName' => $user->user_firstname,
136
+ 'LastName' => $user->user_lastname,
137
+ 'Email' => $user->user_email,
138
+ 'Roles' => $role ? $role : 'none',
139
+ ),
140
+ ), array($this, 'MustNotContainCreateUser'));
141
+ }
142
+
143
+ public function MustNotContainCreateUser(WSAL_AlertManager $mgr){
144
+ return !$mgr->WillTrigger(4012);
145
+ }
146
+ }
classes/Sensors/Widgets.php ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Sensors_Widgets extends WSAL_AbstractSensor {
4
+
5
+ public function HookEvents() {
6
+ add_action('widgets_init', array($this, 'EventWidgetMove'));
7
+ if(is_admin())add_action('init', array($this, 'EventWidgetPostMove'));
8
+ add_action('sidebar_admin_setup', array($this, 'EventWidgetActivity'));
9
+ }
10
+
11
+ protected $_WidgetMoveData = null;
12
+
13
+ public function EventWidgetMove(){
14
+ if(isset($_POST) && !empty($_POST['sidebars']))
15
+ {
16
+ $crtSidebars = $_POST['sidebars'];
17
+ $sidebars = array();
18
+ foreach ( $crtSidebars as $key => $val )
19
+ {
20
+ $sb = array();
21
+ if ( !empty($val) )
22
+ {
23
+ $val = explode(',', $val);
24
+ foreach ( $val as $k => $v )
25
+ {
26
+ if ( strpos($v, 'widget-') === false ) continue;
27
+ $sb[$k] = substr($v, strpos($v, '_') + 1);
28
+ }
29
+ }
30
+ $sidebars[$key] = $sb;
31
+ }
32
+ $crtSidebars = $sidebars;
33
+ $dbSidebars = get_option('sidebars_widgets');
34
+ $wName = $fromSidebar = $toSidebar = '';
35
+ foreach($crtSidebars as $sidebarName => $values)
36
+ {
37
+ if(is_array($values) && ! empty($values) && isset($dbSidebars[$sidebarName]))
38
+ {
39
+ foreach($values as $widgetName)
40
+ {
41
+ if(! in_array($widgetName, $dbSidebars[$sidebarName]))
42
+ {
43
+ $toSidebar = $sidebarName;
44
+ $wName = $widgetName;
45
+ foreach($dbSidebars as $name => $v)
46
+ {
47
+ if(is_array($v) && !empty($v) && in_array($widgetName, $v))
48
+ {
49
+ $fromSidebar = $name;
50
+ break;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ if (empty($wName) || empty($fromSidebar) || empty($toSidebar)) return;
59
+
60
+ if(preg_match('/^sidebar-/', $fromSidebar) || preg_match('/^sidebar-/', $toSidebar)){
61
+ // This option will hold the data needed to trigger the event 2045
62
+ // as at this moment the $wp_registered_sidebars variable is not yet populated
63
+ // so we cannot retrieve the name for sidebar-1 || sidebar-2
64
+ // we will then check for this variable in the EventWidgetPostMove() event
65
+ $this->_WidgetMoveData = array('widget' => $wName, 'from' => $fromSidebar, 'to' => $toSidebar);
66
+ return;
67
+ }
68
+
69
+ $this->plugin->alerts->Trigger(2045, array(
70
+ 'WidgetName' => $wName,
71
+ 'OldSidebar' => $fromSidebar,
72
+ 'NewSidebar' => $toSidebar,
73
+ ));
74
+ }
75
+ }
76
+
77
+ public function EventWidgetPostMove(){
78
+ if($this->_WidgetMoveData){
79
+ $wName = $this->_WidgetMoveData['widget'];
80
+ $fromSidebar = $this->_WidgetMoveData['from'];
81
+ $toSidebar = $this->_WidgetMoveData['to'];
82
+
83
+ global $wp_registered_sidebars;
84
+
85
+ if(preg_match('/^sidebar-/', $fromSidebar))
86
+ $fromSidebar = isset($wp_registered_sidebars[$fromSidebar])
87
+ ? $wp_registered_sidebars[$fromSidebar]['name']
88
+ : $fromSidebar
89
+ ;
90
+ if(preg_match('/^sidebar-/', $toSidebar))
91
+ $toSidebar = isset($wp_registered_sidebars[$toSidebar])
92
+ ? $wp_registered_sidebars[$toSidebar]['name']
93
+ : $toSidebar
94
+ ;
95
+
96
+ $this->plugin->alerts->Trigger(2045, array(
97
+ 'WidgetName' => $wName,
98
+ 'OldSidebar' => $fromSidebar,
99
+ 'NewSidebar' => $toSidebar,
100
+ ));
101
+ }
102
+ }
103
+
104
+ public function EventWidgetActivity(){
105
+ if(!isset($_POST) || !isset($_POST['widget-id']) || empty($_POST['widget-id'])){
106
+ return;
107
+ }
108
+
109
+ $postData = $_POST;
110
+ global $wp_registered_sidebars;
111
+ $canCheckSidebar = (empty($wp_registered_sidebars) ? false : true);
112
+
113
+ switch(true){
114
+
115
+ // added widget
116
+ case isset($postData['add_new']) && $postData['add_new'] == 'multi':
117
+ $sidebar = $postData['sidebar'];
118
+ if($canCheckSidebar && preg_match('/^sidebar-/', $sidebar)){
119
+ $sidebar = $wp_registered_sidebars[$sidebar]['name'];
120
+ }
121
+ $this->plugin->alerts->Trigger(2042, array(
122
+ 'WidgetName' => $postData['id_base'],
123
+ 'Sidebar' => $sidebar,
124
+ ));
125
+ break;
126
+
127
+ // deleted widget
128
+ case isset($postData['delete_widget']) && intval($postData['delete_widget']) == 1:
129
+ $sidebar = $postData['sidebar'];
130
+ if($canCheckSidebar && preg_match('/^sidebar-/',$sidebar)){
131
+ $sidebar = $wp_registered_sidebars[$sidebar]['name'];
132
+ }
133
+ $this->plugin->alerts->Trigger(2044, array(
134
+ 'WidgetName' => $postData['id_base'],
135
+ 'Sidebar' => $sidebar,
136
+ ));
137
+ break;
138
+
139
+ // modified widget
140
+ case isset($postData['id_base']) && !empty($postData['id_base']):
141
+ $wId = 0;
142
+ if(!empty($postData['multi_number'])){
143
+ $wId = intval($postData['multi_number']);
144
+ }elseif(!empty($postData['widget_number'])){
145
+ $wId = intval($postData['widget_number']);
146
+ }
147
+ if(empty($wId))return;
148
+
149
+ $wName = $postData['id_base'];
150
+ $sidebar = $postData['sidebar'];
151
+ $wData = isset($postData["widget-$wName"][$wId])
152
+ ? $postData["widget-$wName"][$wId]
153
+ : null;
154
+
155
+ if(empty($wData))return;
156
+
157
+ // get info from db
158
+ $wdbData = get_option("widget_".$wName);
159
+ if(empty($wdbData[$wId]))return;
160
+
161
+ // transform 'on' -> 1
162
+ foreach($wData as $k => $v)if($v == 'on')$wData[$k] = 1;
163
+
164
+ // compare - checks for any changes inside widgets
165
+ $diff = array_diff_assoc($wData, $wdbData[$wId]);
166
+ $count = count($diff);
167
+ if($count > 0){
168
+ if($canCheckSidebar && preg_match("/^sidebar-/",$sidebar)){
169
+ $sidebar = $wp_registered_sidebars[$sidebar]['name'];
170
+ }
171
+ $this->plugin->alerts->Trigger(2043, array(
172
+ 'WidgetName' => $wName,
173
+ 'Sidebar' => $sidebar,
174
+ ));
175
+ }
176
+ break;
177
+
178
+ }
179
+ }
180
+ }
classes/Settings.php ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Settings {
4
+ /**
5
+ * @var WpSecurityAuditLog
6
+ */
7
+ protected $_plugin;
8
+
9
+ const OPT_PRFX = 'wsal-';
10
+
11
+ public function __construct(WpSecurityAuditLog $plugin){
12
+ $this->_plugin = $plugin;
13
+ }
14
+
15
+ protected function IsMultisite(){
16
+ return function_exists('is_multisite') && is_multisite();
17
+ }
18
+
19
+ protected function GetGlobalOption($option, $default = false){
20
+ $fn = $this->IsMultisite() ? 'get_site_option' : 'get_option';
21
+ return $fn($option, $default);
22
+ }
23
+
24
+ protected function SetGlobalOption($option, $value){
25
+ $fn = $this->IsMultisite() ? 'update_site_option' : 'update_option';
26
+ return $fn($option, $value);
27
+ }
28
+
29
+ protected function GetLocalOption($option, $default = false){
30
+ $result = get_user_option($option, get_current_user_id());
31
+ return $result === false ? $default : $result;
32
+ }
33
+
34
+ protected function SetLocalOption($option, $value){
35
+ update_user_option(get_current_user_id(), $option, $value, false);
36
+ }
37
+
38
+ const OPT_DEV_DATA_INSPECTOR = 'd';
39
+ const OPT_DEV_PHP_ERRORS = 'p';
40
+ const OPT_DEV_REQUEST_LOG = 'r';
41
+ const OPT_DEV_SANDBOX_PAGE = 's';
42
+
43
+ protected $_devoption = null;
44
+
45
+ /**
46
+ * @return array Array of developer options to be enabled by default.
47
+ */
48
+ public function GetDefaultDevOptions(){
49
+ return array();
50
+ }
51
+
52
+ /**
53
+ * Returns whether a developer option is enabled or not.
54
+ * @param string $option See self::OPT_DEV_* constants.
55
+ * @return boolean If option is enabled or not.
56
+ */
57
+ public function IsDevOptionEnabled($option){
58
+ if(is_null($this->_devoption)){
59
+ $this->_devoption = $this->GetGlobalOption(
60
+ self::OPT_PRFX . 'dev-options',
61
+ implode(',', $this->GetDefaultDevOptions())
62
+ );
63
+ $this->_devoption = explode(',', $this->_devoption);
64
+ }
65
+ return in_array($option, $this->_devoption);
66
+ }
67
+
68
+ /**
69
+ * Sets whether a developer option is enabled or not.
70
+ * @param string $option See self::OPT_DEV_* constants.
71
+ * @param boolean $enabled If option should be enabled or not.
72
+ */
73
+ public function SetDevOptionEnabled($option, $enabled){
74
+ // make sure options have been loaded
75
+ $this->IsDevOptionEnabled('');
76
+ // remove option if it exists
77
+ while(($p = array_search($option, $this->_devoption)) !== false)
78
+ unset($this->_devoption[$p]);
79
+ // add option if callee wants it enabled
80
+ if($enabled)
81
+ $this->_devoption[] = $option;
82
+ // commit option
83
+ $this->SetGlobalOption(
84
+ self::OPT_PRFX . 'dev-options',
85
+ implode(',', $this->_devoption)
86
+ );
87
+ }
88
+
89
+ /**
90
+ * Remove all enabled developer options.
91
+ */
92
+ public function ClearDevOptions(){
93
+ $this->_devoption = array();
94
+ $this->SetGlobalOption(self::OPT_PRFX . 'dev-options', '');
95
+ }
96
+
97
+ /**
98
+ * @return boolean Whether to enable data inspector or not.
99
+ */
100
+ public function IsDataInspectorEnabled(){
101
+ return $this->IsDevOptionEnabled(self::OPT_DEV_DATA_INSPECTOR);
102
+ }
103
+
104
+ /**
105
+ * @return boolean Whether to PHP error logging or not.
106
+ */
107
+ public function IsPhpErrorLoggingEnabled(){
108
+ return $this->IsDevOptionEnabled(self::OPT_DEV_PHP_ERRORS);
109
+ }
110
+
111
+ /**
112
+ * @return boolean Whether to log requests to file or not.
113
+ */
114
+ public function IsRequestLoggingEnabled(){
115
+ return $this->IsDevOptionEnabled(self::OPT_DEV_REQUEST_LOG);
116
+ }
117
+
118
+ /**
119
+ * @return boolean Whether PHP sandbox page is enabled or not.
120
+ */
121
+ public function IsSandboxPageEnabled(){
122
+ return $this->IsDevOptionEnabled(self::OPT_DEV_SANDBOX_PAGE);
123
+ }
124
+
125
+ /**
126
+ * @return boolean Whether dashboard widgets are enabled or not.
127
+ */
128
+ public function IsWidgetsEnabled(){
129
+ return !$this->GetGlobalOption(self::OPT_PRFX . 'disable-widgets');
130
+ }
131
+
132
+ /**
133
+ * @param boolean $newvalue Whether dashboard widgets are enabled or not.
134
+ */
135
+ public function SetWidgetsEnabled($newvalue){
136
+ $this->SetGlobalOption(self::OPT_PRFX . 'disable-widgets', !$newvalue);
137
+ }
138
+
139
+ /**
140
+ * @return boolean Whether alerts in audit log view refresh automatically or not.
141
+ */
142
+ public function IsRefreshAlertsEnabled(){
143
+ return !$this->GetGlobalOption(self::OPT_PRFX . 'disable-refresh');
144
+ }
145
+
146
+ /**
147
+ * @param boolean $newvalue Whether alerts in audit log view refresh automatically or not.
148
+ */
149
+ public function SetRefreshAlertsEnabled($newvalue){
150
+ $this->SetGlobalOption(self::OPT_PRFX . 'disable-refresh', !$newvalue);
151
+ }
152
+
153
+ /**
154
+ * @return int Maximum number of alerts to show in dashboard widget.
155
+ */
156
+ public function GetDashboardWidgetMaxAlerts(){
157
+ return 5;
158
+ }
159
+
160
+ /**
161
+ * @return int The maximum number of alerts allowable.
162
+ */
163
+ public function GetMaxAllowedAlerts(){
164
+ return 5000;
165
+ }
166
+
167
+ /**
168
+ * @return string The default pruning date.
169
+ */
170
+ public function GetDefaultPruningDate(){
171
+ return '1 month';
172
+ }
173
+
174
+ protected $_pruning = 0;
175
+
176
+ /**
177
+ * @return string The current pruning date.
178
+ */
179
+ public function GetPruningDate(){
180
+ if(!$this->_pruning){
181
+ $this->_pruning = $this->GetGlobalOption(self::OPT_PRFX . 'pruning-date');
182
+ if(!strtotime($this->_pruning))
183
+ $this->_pruning = $this->GetDefaultPruningDate();
184
+ }
185
+ return $this->_pruning;
186
+ }
187
+
188
+ /**
189
+ * @param string $newvalue The new pruning date.
190
+ */
191
+ public function SetPruningDate($newvalue){
192
+ if(strtotime($newvalue)){
193
+ $this->SetGlobalOption(self::OPT_PRFX . 'pruning-date', $newvalue);
194
+ $this->_pruning = $newvalue;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * @return integer Maximum number of alerts to keep.
200
+ */
201
+ public function GetPruningLimit(){
202
+ $val = (int)$this->GetGlobalOption(self::OPT_PRFX . 'pruning-limit');
203
+ return $val ? $val : $this->GetMaxAllowedAlerts();
204
+ }
205
+
206
+ /**
207
+ * @param integer $newvalue The new maximum number of alerts.
208
+ */
209
+ public function SetPruningLimit($newvalue){
210
+ $newvalue = max(min((int)$newvalue, $this->GetMaxAllowedAlerts()), 1);
211
+ $this->SetGlobalOption(self::OPT_PRFX . 'pruning-limit', $newvalue);
212
+ }
213
+
214
+ protected $_disabled = null;
215
+
216
+ public function GetDefaultDisabledAlerts(){
217
+ return array(); //array(0000, 0003, 0005);
218
+ }
219
+
220
+ /**
221
+ * @return array IDs of disabled alerts.
222
+ */
223
+ public function GetDisabledAlerts(){
224
+ if(!$this->_disabled){
225
+ $this->_disabled = implode(',', $this->GetDefaultDisabledAlerts());
226
+ $this->_disabled = $this->GetGlobalOption(self::OPT_PRFX . 'disabled-alerts', $this->_disabled);
227
+ $this->_disabled = explode(',', $this->_disabled);
228
+ $this->_disabled = array_map('intval', $this->_disabled);
229
+ }
230
+ return $this->_disabled;
231
+ }
232
+
233
+ /**
234
+ * @param array $types IDs alerts to disable.
235
+ */
236
+ public function SetDisabledAlerts($types){
237
+ $this->_disabled = array_unique(array_map('intval', $types));
238
+ $this->SetGlobalOption(self::OPT_PRFX . 'disabled-alerts', implode(',', $this->_disabled));
239
+ }
240
+
241
+ protected $_viewers = null;
242
+
243
+ public function SetAllowedPluginViewers($usersOrRoles){
244
+ $this->_viewers = $usersOrRoles;
245
+ $this->SetGlobalOption(self::OPT_PRFX . 'plugin-viewers', implode(',', $this->_viewers));
246
+ }
247
+
248
+ public function GetAllowedPluginViewers(){
249
+ if(is_null($this->_viewers)){
250
+ $this->_viewers = array_unique(array_filter(explode(',', $this->GetGlobalOption(self::OPT_PRFX . 'plugin-viewers'))));
251
+ }
252
+ return $this->_viewers;
253
+ }
254
+
255
+ protected $_editors = null;
256
+
257
+ public function SetAllowedPluginEditors($usersOrRoles){
258
+ $this->_editors = $usersOrRoles;
259
+ $this->SetGlobalOption(self::OPT_PRFX . 'plugin-editors', implode(',', $this->_editors));
260
+ }
261
+
262
+ public function GetAllowedPluginEditors(){
263
+ if(is_null($this->_editors)){
264
+ $this->_editors = array_unique(array_filter(explode(',', $this->GetGlobalOption(self::OPT_PRFX . 'plugin-editors'))));
265
+ }
266
+ return $this->_editors;
267
+ }
268
+
269
+ protected $_perpage = null;
270
+
271
+ public function SetViewPerPage($newvalue){
272
+ $this->_perpage = max($newvalue, 1);
273
+ $this->SetGlobalOption(self::OPT_PRFX . 'items-per-page', $this->_perpage);
274
+ }
275
+
276
+ public function GetViewPerPage(){
277
+ if(is_null($this->_perpage)){
278
+ $this->_perpage = (int)$this->GetGlobalOption(self::OPT_PRFX . 'items-per-page', 10);
279
+ }
280
+ return $this->_perpage;
281
+ }
282
+
283
+ /**
284
+ * @param string $action Type of action, either 'view' or 'edit'.
285
+ * @return boolean If user has access or not.
286
+ */
287
+ public function CurrentUserCan($action){
288
+ return $this->UserCan(wp_get_current_user(), $action);
289
+ }
290
+
291
+ /**
292
+ * @return string[] List of superadmin usernames.
293
+ */
294
+ protected function GetSuperAdmins(){
295
+ return $this->IsMultisite() ? get_super_admins() : array();
296
+ }
297
+
298
+ /**
299
+ * @return string[] List of admin usernames.
300
+ */
301
+ protected function GetAdmins(){
302
+ if($this->IsMultisite()){
303
+ // see: https://gist.github.com/1508426/65785a15b8638d43a9905effb59e4d97319ef8f8
304
+ global $wpdb;
305
+ $cap = get_current_blog_id();
306
+ $cap = ($cap < 2) ? 'wp_capabilities' : "wp_{$cap}_capabilities";
307
+ $sql = "SELECT DISTINCT $wpdb->users.user_login"
308
+ . " FROM $wpdb->users"
309
+ . " INNER JOIN $wpdb->usermeta ON ($wpdb->users.ID = $wpdb->usermeta.user_id )"
310
+ . " WHERE $wpdb->usermeta.meta_key = '$cap'"
311
+ . " AND CAST($wpdb->usermeta.meta_value AS CHAR) LIKE '%\"administrator\"%'";
312
+ return $wpdb->get_col($sql);
313
+ }else{
314
+ $result = array();
315
+ $query = 'role=administrator&fields[]=user_login';
316
+ foreach (get_users($query) as $user) $result[] = $user->user_login;
317
+ return $result;
318
+ }
319
+ }
320
+
321
+ /**
322
+ * @param integer|WP_user $user User object to check.
323
+ * @param string $action Type of action, either 'view' or 'edit'.
324
+ * @return boolean If user has access or not.
325
+ */
326
+ public function UserCan($user, $action){
327
+ if(is_int($user))$user = get_userdata($user);
328
+ $allowed = array();
329
+
330
+ switch($action){
331
+ case 'view':
332
+ $allowed = $this->GetAllowedPluginViewers();
333
+ $allowed = array_merge($allowed, $this->GetAllowedPluginEditors());
334
+ $allowed = array_merge($allowed, $this->GetSuperAdmins());
335
+ $allowed = array_merge($allowed, $this->GetAdmins());
336
+ break;
337
+ case 'edit':
338
+ $allowed = $this->GetAllowedPluginEditors();
339
+ $allowed = array_merge($allowed, $this->IsMultisite() ?
340
+ $this->GetSuperAdmins() : $this->GetAdmins()
341
+ );
342
+ break;
343
+ default:
344
+ throw new Exception('Unknown action "'.$action.'".');
345
+ }
346
+
347
+ $check = array_merge(
348
+ $user->roles,
349
+ array($user->user_login)
350
+ );
351
+
352
+ if(is_multisite()){
353
+ $allowed = array_merge($allowed, get_super_admins());
354
+ }else{
355
+ $allowed[] = 'administrator';
356
+ }
357
+
358
+ foreach($check as $item){
359
+ if(in_array($item, $allowed)){
360
+ return true;
361
+ }
362
+ }
363
+
364
+ return false;
365
+ }
366
+ }
classes/ViewManager.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_ViewManager {
4
+
5
+ /**
6
+ * @var WSAL_AbstractView[]
7
+ */
8
+ public $views = array();
9
+
10
+ /**
11
+ * @var WpSecurityAuditLog
12
+ */
13
+ protected $_plugin;
14
+
15
+ public function __construct(WpSecurityAuditLog $plugin){
16
+ $this->_plugin = $plugin;
17
+
18
+ // load views
19
+ foreach(glob(dirname(__FILE__) . '/Views/*.php') as $file){
20
+ $class = $this->_plugin->GetClassFileClassName($file);
21
+ $tmp = new $class($this->_plugin);
22
+ $this->views[] = $tmp;
23
+ }
24
+
25
+ // order views by weight
26
+ usort($this->views, array($this, 'OrderByWeight'));
27
+
28
+ // add menus
29
+ add_action('admin_menu', array($this, 'AddAdminMenus'));
30
+ add_action('network_admin_menu', array($this, 'AddAdminMenus'));
31
+
32
+ // add plugin shortcut links
33
+ add_filter('plugin_action_links_' . $plugin->GetBaseName(), array($this, 'AddPluginShortcuts'));
34
+
35
+ // render header
36
+ add_action('admin_enqueue_scripts', array($this, 'RenderViewHeader'));
37
+
38
+ // render footer
39
+ add_action('admin_footer', array($this, 'RenderViewFooter'));
40
+ }
41
+
42
+ public function OrderByWeight(WSAL_AbstractView $a, WSAL_AbstractView $b){
43
+ $wa = $a->GetWeight();
44
+ $wb = $b->GetWeight();
45
+ switch(true){
46
+ case $wa < $wb:
47
+ return -1;
48
+ case $wa > $wb:
49
+ return 1;
50
+ default:
51
+ return 0;
52
+ }
53
+ }
54
+
55
+ public function AddAdminMenus(){
56
+ if($this->_plugin->settings->CurrentUserCan('view') && count($this->views)){
57
+ // add main menu
58
+ add_menu_page(
59
+ 'WP Security Audit Log',
60
+ 'Audit Log',
61
+ 'read', // no capability requirement
62
+ $this->views[0]->GetSafeViewName(),
63
+ array($this, 'RenderViewBody'),
64
+ $this->views[0]->GetIcon()
65
+ );
66
+
67
+ // add menu items
68
+ foreach($this->views as $view){
69
+ if($view->IsVisible()){
70
+ add_submenu_page(
71
+ $this->views[0]->GetSafeViewName(),
72
+ $view->GetTitle(),
73
+ $view->GetName(),
74
+ 'read', // no capability requirement
75
+ $view->GetSafeViewName(),
76
+ array($this, 'RenderViewBody'),
77
+ $view->GetIcon()
78
+ );
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ public function AddPluginShortcuts($old_links){
85
+ $new_links = array();
86
+ foreach($this->views as $view){
87
+ if($view->HasPluginShortcutLink()){
88
+ $new_links[] =
89
+ '<a href="'
90
+ . admin_url('admin.php?page='
91
+ . $view->GetSafeViewName()
92
+ ) . '">'
93
+ . $view->GetName()
94
+ . '</a>';
95
+ }
96
+ }
97
+ return array_merge($new_links, $old_links);
98
+ }
99
+
100
+ protected function GetBackendPageIndex(){
101
+ if(isset($_REQUEST['page']))
102
+ foreach($this->views as $i => $view)
103
+ if($_REQUEST['page'] == $view->GetSafeViewName())
104
+ return $i;
105
+ return 0;
106
+ }
107
+
108
+ public function RenderViewHeader(){
109
+ $view_id = $this->GetBackendPageIndex();
110
+ $this->views[$view_id]->Header();
111
+ }
112
+
113
+ public function RenderViewFooter(){
114
+ $view_id = $this->GetBackendPageIndex();
115
+ $this->views[$view_id]->Footer();
116
+ }
117
+
118
+ public function RenderViewBody(){
119
+ $view_id = $this->GetBackendPageIndex();
120
+ ?><div class="wrap">
121
+ <div id="icon-plugins" class="icon32"><br></div>
122
+ <h2><?php _e($this->views[$view_id]->GetTitle()); ?></h2>
123
+ <?php $this->views[$view_id]->Render(); ?>
124
+ </div><?php
125
+ }
126
+
127
+ }
classes/Views/About.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Views_About extends WSAL_AbstractView {
4
+
5
+ public function GetTitle() {
6
+ return 'About WP Security Audit Log';
7
+ }
8
+
9
+ public function GetIcon() {
10
+ return 'dashicons-editor-help';
11
+ }
12
+
13
+ public function GetName() {
14
+ return 'About';
15
+ }
16
+
17
+ public function GetWeight(){
18
+ return 4;
19
+ }
20
+
21
+ public function Render(){
22
+ ?><div class="metabox-holder" style="position: relative;">
23
+
24
+ <div class="postbox" style="margin-right: 270px;">
25
+ <!--h3 class="hndl"><span>About WP Security Audit Log</span></h3-->
26
+ <div class="inside">
27
+ <div class="activity-block">
28
+ 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.
29
+
30
+ <h2>Keep A WordPress Security Audit Log & Identify WordPress Security Issues</h2>
31
+ <p>
32
+ 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:
33
+ </p>
34
+ <ul style="list-style-type: disc; margin-left: 2.5em; list-style-position: outside;">
35
+ <li>User creates a new user or a new user is registered</li>
36
+ <li>Existing user changes the role, password or other properties of another user</li>
37
+ <li>Existing user on a WordPress multisite network is added to a site</li>
38
+ <li>User uploads or deletes a file, changes a password or email address</li>
39
+ <li>User installs, activates, deactivates, upgrades or uninstalls a plugin</li>
40
+ <li>User creates, modifies or deletes a new post, page, category or a custom post type</li>
41
+ <li>User installs or activates a WordPress theme</li>
42
+ <li>User adds, modifies or deletes a widget</li>
43
+ <li>User uses the dashboard file editor</li>
44
+ <li>WordPress settings are changed</li>
45
+ <li>Failed login attempts</li>
46
+ <li>and much more&hellip;</li>
47
+ </ul>
48
+ <br/>
49
+ Refer to the complete list of <a href="http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/security-audit-alerts-logs/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">WordPress Security Alerts</a> for more information.
50
+ </div>
51
+ </div>
52
+ </div>
53
+
54
+ <div style="position: absolute; right: 70px; width: 180px; top: 10px;">
55
+ <div class="postbox">
56
+ <h3 class="hndl"><span>WP Password Policy Manager</span></h3>
57
+ <div class="inside">
58
+ <p>
59
+ Easily configure WordPress password policies and ensure users use strong passwords with our plugin WP Password Policy Manager.
60
+ </p>
61
+ <a class="button button-primary" href="http://wordpress.org/plugins/wp-password-policy-manager/" target="_blank">Download</a>
62
+ </div>
63
+ </div>
64
+ <div class="postbox">
65
+ <h3 class="hndl"><span>WP Security Audit Log in your Language!</span></h3>
66
+ <div class="inside">
67
+ If you are interested in translating our plugin please drop us an email on
68
+ <a href="mailto:plugins@wpwhitesecurity.com">plugins@wpwhitesecurity.com</a>.
69
+ </div>
70
+ </div>
71
+ <div class="postbox">
72
+ <h3 class="hndl"><span>WordPress Security Services</span></h3>
73
+ <div class="inside">
74
+ Professional WordPress security services provided by WP White Security
75
+ <ul>
76
+ <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>
77
+ <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>
78
+ <li><a href="http://www.wpwhitesecurity.com/wordpress-security-services/wordpress-hacker-attack-malware-virus-removal-services/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">Hack Cleanup</a></li>
79
+ <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>
80
+ </ul>
81
+ </div>
82
+ </div>
83
+ </div>
84
+ </div><?php
85
+ }
86
+
87
+ }
classes/Views/AuditLog.php ADDED
@@ -0,0 +1,390 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Views_AuditLog extends WSAL_AbstractView {
4
+
5
+ protected $_listview;
6
+
7
+ public function __construct(WpSecurityAuditLog $plugin) {
8
+ parent::__construct($plugin);
9
+ $this->_listview = new WSAL_Views_AuditLogList_Internal($plugin);
10
+ add_action('wp_ajax_AjaxInspector', array($this, 'AjaxInspector'));
11
+ add_action('wp_ajax_AjaxRefresh', array($this, 'AjaxRefresh'));
12
+ add_action('wp_ajax_AjaxSetIpp', array($this, 'AjaxSetIpp'));
13
+ }
14
+
15
+ public function HasPluginShortcutLink(){
16
+ return true;
17
+ }
18
+
19
+ public function GetTitle() {
20
+ return 'Audit Log Viewer';
21
+ }
22
+
23
+ public function GetIcon() {
24
+ return $this->_wpversion < 3.8
25
+ ? $this->_plugin->GetBaseUrl() . '/img/logo-main-menu.png'
26
+ : 'dashicons-welcome-view-site';
27
+ }
28
+
29
+ public function GetName() {
30
+ return 'Audit Log Viewer';
31
+ }
32
+
33
+ public function GetWeight(){
34
+ return 1;
35
+ }
36
+
37
+ public function Render(){
38
+ if(!$this->_plugin->settings->CurrentUserCan('view')){
39
+ wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
40
+ }
41
+
42
+ ?><form id="audit-log-viewer" method="post">
43
+ <input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
44
+ <input type="hidden" id="wsal-cbid" name="wsal-cbid" value="<?php echo esc_attr(isset($_REQUEST['wsal-cbid']) ? $_REQUEST['wsal-cbid'] : ''); ?>" />
45
+ <?php $this->_listview->prepare_items(); ?>
46
+ <?php $this->_listview->display(); ?>
47
+ </form><?php
48
+
49
+ ?><script type="text/javascript">
50
+ jQuery(document).ready(function(){
51
+ WsalAuditLogInit(<?php echo json_encode(array(
52
+ 'ajaxurl' => admin_url('admin-ajax.php'),
53
+ 'tr8n' => array(
54
+ 'numofitems' => __('Please enter the number of alerts you would like to see on one page:'),
55
+ ),
56
+ 'autorefresh' => array(
57
+ 'enabled' => $this->_plugin->settings->IsRefreshAlertsEnabled(),
58
+ 'token' => (int)WSAL_DB_Occurrence::Count(),
59
+ ),
60
+ )); ?>);
61
+ });
62
+ </script><?php
63
+ }
64
+
65
+ public function AjaxInspector(){
66
+ if(!$this->_plugin->settings->CurrentUserCan('view'))
67
+ die('Access Denied.');
68
+ if(!isset($_REQUEST['occurrence']))
69
+ die('Occurrence parameter expected.');
70
+ $occ = new WSAL_DB_Occurrence();
71
+ $occ->Load('id = %d', array((int)$_REQUEST['occurrence']));
72
+
73
+ echo '<!DOCTYPE html><html><head>';
74
+ echo '<link rel="stylesheet" id="open-sans-css" href="' . $this->_plugin->GetBaseUrl() . '/css/nice_r.css" type="text/css" media="all">';
75
+ echo '<script type="text/javascript" src="'.$this->_plugin->GetBaseUrl() . '/js/nice_r.js"></script>';
76
+ echo '<style type="text/css">';
77
+ echo 'html, body { margin: 0; padding: 0; }';
78
+ echo '.nice_r { position: absolute; padding: 8px; }';
79
+ echo '.nice_r a { overflow: visible; }';
80
+ echo '</style>';
81
+ echo '</head><body>';
82
+ $nicer = new WSAL_Nicer($occ->GetMetaArray());
83
+ $nicer->render();
84
+ echo '</body></html>';
85
+ die;
86
+ }
87
+
88
+ public function AjaxRefresh(){
89
+ if(!$this->_plugin->settings->CurrentUserCan('view'))
90
+ die('Access Denied.');
91
+ if(!isset($_REQUEST['logcount']))
92
+ die('Log count parameter expected.');
93
+
94
+ $old = (int)$_REQUEST['logcount'];
95
+ $max = 40; // 40*500msec = 20sec
96
+
97
+ do{
98
+ $new = WSAL_DB_Occurrence::Count();
99
+ usleep(500000); // 500msec
100
+ }while(($old == $new) && (--$max > 0));
101
+
102
+ echo $old == $new ? 'false' : $new;
103
+ die;
104
+ }
105
+
106
+ public function AjaxSetIpp(){
107
+ if(!$this->_plugin->settings->CurrentUserCan('view'))
108
+ die('Access Denied.');
109
+ if(!isset($_REQUEST['count']))
110
+ die('Count parameter expected.');
111
+ $this->_plugin->settings->SetViewPerPage((int)$_REQUEST['count']);
112
+ die;
113
+ }
114
+
115
+ public function Header(){
116
+ add_thickbox();
117
+ wp_enqueue_style(
118
+ 'auditlog',
119
+ $this->_plugin->GetBaseUrl() . '/css/auditlog.css',
120
+ array(),
121
+ filemtime($this->_plugin->GetBaseDir() . '/css/auditlog.css')
122
+ );
123
+ }
124
+
125
+ public function Footer() {
126
+ wp_enqueue_script(
127
+ 'auditlog',
128
+ $this->_plugin->GetBaseUrl() . '/js/auditlog.js',
129
+ array(),
130
+ filemtime($this->_plugin->GetBaseDir() . '/js/auditlog.js')
131
+ );
132
+ }
133
+
134
+ }
135
+
136
+ require_once(ABSPATH . 'wp-admin/includes/admin.php');
137
+ require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
138
+
139
+ class WSAL_Views_AuditLogList_Internal extends WP_List_Table {
140
+
141
+ /**
142
+ * @var WpSecurityAuditLog
143
+ */
144
+ protected $_plugin;
145
+
146
+ public function __construct($plugin){
147
+ $this->_plugin = $plugin;
148
+
149
+ parent::__construct(array(
150
+ 'singular' => 'log',
151
+ 'plural' => 'logs',
152
+ 'ajax' => true,
153
+ 'screen' => 'interval-list',
154
+ ));
155
+ }
156
+
157
+ public function no_items(){
158
+ _e('No events so far.');
159
+ }
160
+
161
+ public function extra_tablenav($which){
162
+ // items-per-page widget
163
+ $o = __('Other');
164
+ $p = $this->_plugin->settings->GetViewPerPage();
165
+ $items = array($o, 5, 10, 15, 30, 50);
166
+ if (!in_array($p, $items)) $items[] = $p;
167
+ if ($p == $o || $p == 0) $p = $o[1]; // a sane default if things goes bust
168
+
169
+ ?><div class="wsal-ipp wsal-ipp-<?php echo $which; ?>">
170
+ <?php _e('Show '); ?>
171
+ <select class="wsal-ipps" onfocus="WsalIppsFocus(value);" onchange="WsalIppsChange(value);">
172
+ <?php foreach($items as $item){ ?>
173
+ <option
174
+ value="<?php echo is_string($item) ? '' : $item; ?>"
175
+ <?php if($item == $p)echo 'selected="selected"'; ?>><?php
176
+ echo $item;
177
+ ?></option>
178
+ <?php } ?>
179
+ </select>
180
+ <?php _e(' Items'); ?>
181
+ </div><?php
182
+
183
+ // show site alerts widget
184
+ if($this->is_multisite() && $this->is_main_blog()){
185
+ // TODO should I check wp_is_large_network()?
186
+ $curr = $this->get_view_site_id();
187
+ $sites = wp_get_sites();
188
+ ?><div class="wsal-ssa wsal-ssa-<?php echo $which; ?>">
189
+ <select class="wsal-ssas" onchange="WsalSsasChange(value);">
190
+ <option value="0"><?php _e('All Sites'); ?></option>
191
+ <?php foreach($sites as $site){ ?>
192
+ <?php $info = get_blog_details($site['blog_id'], true); ?>
193
+ <option
194
+ value="<?php echo $info->blog_id; ?>"
195
+ <?php if($info->blog_id == $curr)echo 'selected="selected"'; ?>><?php
196
+ echo esc_html($info->blogname) . ' (' . esc_html($info->domain) . ')';
197
+ ?></option>
198
+ <?php } ?>
199
+ </select>
200
+ </div><?php
201
+ }
202
+ }
203
+
204
+ public function get_columns(){
205
+ $cols = array(
206
+ //'cb' => '<input type="checkbox" />',
207
+ //'read' => 'Read',
208
+ 'type' => 'Code',
209
+ 'code' => 'Type',
210
+ 'crtd' => 'Date',
211
+ 'user' => 'Username',
212
+ 'scip' => 'Source IP',
213
+ );
214
+ if($this->is_multisite() && $this->is_main_blog() && !$this->is_specific_view()){
215
+ $cols['site'] = 'Site';
216
+ }
217
+ $cols['mesg'] = 'Message';
218
+ if($this->_plugin->settings->IsDataInspectorEnabled()){
219
+ $cols['data'] = '';
220
+ }
221
+ return $cols;
222
+ }
223
+
224
+ public function column_cb(WSAL_DB_Occurrence $item){
225
+ return '<input type="checkbox" value="'.$item->id.'" '
226
+ . 'name="'.esc_attr($this->_args['singular']).'[]"/>';
227
+ }
228
+
229
+ public function get_sortable_columns(){
230
+ return array(
231
+ 'read' => array('is_read', false),
232
+ 'code' => array('code', false),
233
+ 'type' => array('alert_id', false),
234
+ 'crtd' => array('created_on', true),
235
+ 'user' => array('user', false),
236
+ 'scip' => array('scip', false),
237
+ 'site' => array('site', false),
238
+ );
239
+ }
240
+
241
+ public function column_default(WSAL_DB_Occurrence $item, $column_name){
242
+ switch($column_name){
243
+ case 'read':
244
+ return '<span class="log-read log-read-'
245
+ . ($item->is_read ? 'old' : 'new')
246
+ . '" title="Click to toggle."></span>';
247
+ case 'type':
248
+ return str_pad($item->alert_id, 4, '0', STR_PAD_LEFT);
249
+ case 'code':
250
+ $code = $this->_plugin->alerts->GetAlert($item->alert_id);
251
+ $code = $code ? $code->code : 0;
252
+ $const = (object)array('name' => 'E_UNKNOWN', 'value' => 0, 'description' => 'Unknown error code.');
253
+ $const = $this->_plugin->constants->GetConstantBy('value', $code, $const);
254
+ return '<span class="log-type log-type-' . $const->value
255
+ . '" title="' . esc_html($const->name . ': ' . $const->description) . '"></span>';
256
+ case 'crtd':
257
+ return $item->created_on ? date('Y-m-d h:i:s A', $item->created_on) : '<i>unknown</i>';
258
+ case 'user':
259
+ $user = $item->GetUsername();
260
+ $usid = get_userdatabylogin($user ? $user : 0);
261
+ $usid = $usid ? $usid->ID : 0;
262
+ $uimg = get_avatar($usid, 96);
263
+ return !is_null($user) ? esc_html($user) : '<i>unknown</i>';
264
+ case 'scip':
265
+ return !is_null($item->GetSourceIP()) ? esc_html($item->GetSourceIP()) : '<i>unknown</i>';
266
+ case 'site':
267
+ $info = get_blog_details($item->site_id, true);
268
+ return !$info ? ('Unknown Site '.$item->site_id)
269
+ : ('<a href="' . esc_attr($info->siteurl) . '">' . esc_html($info->blogname) . '</a>');
270
+ case 'mesg':
271
+ return '<div id="Event' . $item->id . '">' . $item->GetMessage(array($this, 'meta_formatter')) . '</div>';
272
+ case 'data':
273
+ $url = admin_url('admin-ajax.php') . '?action=AjaxInspector&amp;occurrence=' . $item->id;
274
+ return '<a class="more-info thickbox" title="Alert Data Inspector"'
275
+ . ' href="' . $url . '&amp;TB_iframe=true&amp;width=600&amp;height=550">&hellip;</a>';
276
+ default:
277
+ return isset($item->$column_name)
278
+ ? esc_html($item->$column_name)
279
+ : 'Column "' . esc_html($column_name) . '" not found';
280
+ }
281
+ }
282
+
283
+ public function reorder_items_str($a, $b){
284
+ $result = strcmp($a->{$this->_orderby}, $b->{$this->_orderby});
285
+ return ($this->_order === 'asc') ? $result : -$result;
286
+ }
287
+
288
+ public function reorder_items_int($a, $b){
289
+ $result = $a->{$this->_orderby} - $b->{$this->_orderby};
290
+ return ($this->_order === 'asc') ? $result : -$result;
291
+ }
292
+
293
+ public function meta_formatter($name, $value){
294
+ switch(true){
295
+
296
+ case $name == '%Message%':
297
+ return esc_html($value);
298
+
299
+ case strncmp($value, 'http://', 7) === 0:
300
+ case strncmp($value, 'https://', 7) === 0:
301
+ return '<a href="' . esc_html($value) . '"'
302
+ . ' title="' . esc_html($value) . '"'
303
+ . ' target="_blank">'
304
+ . esc_html(parse_url($value, PHP_URL_HOST)) . '/&hellip;/'
305
+ . esc_html(basename(parse_url($value, PHP_URL_PATH)))
306
+ . '</a>';
307
+
308
+ default:
309
+ return '<strong>' . esc_html($value) . '</strong>';
310
+ }
311
+ }
312
+
313
+ protected function is_multisite(){
314
+ return function_exists('is_multisite') && is_multisite();
315
+ }
316
+
317
+ protected function is_main_blog(){
318
+ return get_current_blog_id() == 1;
319
+ }
320
+
321
+ protected function is_specific_view(){
322
+ return isset($_REQUEST['wsal-cbid']) && $_REQUEST['wsal-cbid'] != '0';
323
+ }
324
+
325
+ protected function get_specific_view(){
326
+ return isset($_REQUEST['wsal-cbid']) ? (int)$_REQUEST['wsal-cbid'] : 0;
327
+ }
328
+
329
+ protected function get_view_site_id(){
330
+ switch(true){
331
+
332
+ // non-multisite
333
+ case !function_exists('is_multisite') || !is_multisite():
334
+ return 0;
335
+
336
+ // multisite + main site view
337
+ case $this->is_main_blog() && !$this->is_specific_view():
338
+ return 0;
339
+
340
+ // multisite + switched site view
341
+ case $this->is_main_blog() && $this->is_specific_view():
342
+ return $this->get_specific_view();
343
+
344
+ // multisite + local site view
345
+ default:
346
+ return get_current_blog_id();
347
+
348
+ }
349
+ }
350
+
351
+ public function prepare_items() {
352
+ $per_page = $this->_plugin->settings->GetViewPerPage();
353
+
354
+ $columns = $this->get_columns();
355
+ $hidden = array();
356
+ $sortable = $this->get_sortable_columns();
357
+
358
+ $this->_column_headers = array($columns, $hidden, $sortable);
359
+
360
+ //$this->process_bulk_action();
361
+
362
+ $bid = (int)$this->get_view_site_id();
363
+ $sql = ($bid ? "site_id=$bid" : '1') . ' ORDER BY created_on DESC';
364
+ $data = WSAL_DB_Occurrence::LoadMulti($sql);
365
+
366
+ if(count($data)){
367
+ $this->_orderby = (!empty($_REQUEST['orderby']) && isset($sortable[$_REQUEST['orderby']])) ? $_REQUEST['orderby'] : 'created_on';
368
+ $this->_order = (!empty($_REQUEST['order']) && $_REQUEST['order']=='asc') ? 'asc' : 'desc';
369
+ if(isset($data[0]->{$this->_orderby})){
370
+ $numorder = in_array($this->_orderby, array('code', 'type', 'created_on'));
371
+ usort($data, array($this, $numorder ? 'reorder_items_int' : 'reorder_items_str'));
372
+ }
373
+ }
374
+
375
+ $current_page = $this->get_pagenum();
376
+
377
+ $total_items = count($data);
378
+
379
+ $data = array_slice($data, ($current_page - 1) * $per_page, $per_page);
380
+
381
+ $this->items = $data;
382
+
383
+ $this->set_pagination_args( array(
384
+ 'total_items' => $total_items,
385
+ 'per_page' => $per_page,
386
+ 'total_pages' => ceil($total_items / $per_page)
387
+ ) );
388
+ }
389
+
390
+ }
classes/Views/Help.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Views_Help extends WSAL_AbstractView {
4
+
5
+ public function GetTitle() {
6
+ return 'Help';
7
+ }
8
+
9
+ public function GetIcon() {
10
+ return 'dashicons-sos';
11
+ }
12
+
13
+ public function GetName() {
14
+ return 'Help';
15
+ }
16
+
17
+ public function GetWeight() {
18
+ return 5;
19
+ }
20
+
21
+ public function Render(){
22
+ ?><div class="metabox-holder" style="position: relative;">
23
+
24
+ <div class="postbox" style="margin-right: 270px;">
25
+ <h3 class="hndl"><span>Help</span></h3>
26
+ <div class="inside">
27
+ <div class="activity-block">
28
+ <h2>Plugin Support</h2>
29
+ <p>
30
+ Have you encountered or noticed any issues while using WP Security Audit Log plugin?
31
+ 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.
32
+ </p><p>
33
+ <a class="button" href="http://wordpress.org/support/plugin/wp-security-audit-log" target="_blank">Free Support Forum</a>
34
+ &nbsp;&nbsp;&nbsp;&nbsp;
35
+ <a class="button" href="mailto:plugins@wpwhitesecurity.com" target="_blank">Free Support Email</a>
36
+ </p>
37
+ </div>
38
+
39
+ <div class="activity-block">
40
+ <h2>Plugin Documentation</h2>
41
+ <p>
42
+ For more detailed information about WP Security Audit Log you can visit the official plugin page.
43
+ You can also visit the official list of WordPress Security Alerts for more information about all of the activity you can monitor with WP Security Audit Log.
44
+ </p><p>
45
+ <a class="button" href="http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">Official Plugin Page</a>
46
+ &nbsp;&nbsp;&nbsp;&nbsp;
47
+ <a class="button" href="http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/security-audit-alerts-logs/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">List of WordPress Security Alerts</a>
48
+ </p>
49
+ </div>
50
+
51
+ <div class="activity-block">
52
+ <h2>Need Help Securing WordPress?</h2>
53
+ <p>
54
+ Is your WordPress website hackable?
55
+ If you are not sure contact our WordPress security professionals to audit your WordPress or to simply secure your WordPress website.
56
+ Click on any of the below service buttons for more information.
57
+ </p><p>
58
+ <a class="button" href="http://www.wpwhitesecurity.com/wordpress-security-services/wordpress-security-hardening/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">WordPress Security Hardening</a>
59
+ &nbsp;&nbsp;&nbsp;&nbsp;
60
+ <a class="button" href="http://www.wpwhitesecurity.com/wordpress-security-services/wordpress-security-audit/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">WordPress Security Audit</a>
61
+ </p>
62
+ </div>
63
+
64
+ <div class="">
65
+ <h2>WordPress Security Readings</h2>
66
+ <p>
67
+ New to WordPress security?
68
+ Do not know from where to start or which is the best services for you?
69
+ Visit our WordPress security blog or the WordPress Security category directly for more information and a number of tips and tricks about WordPress security.
70
+ </p>
71
+ <a class="button" href="http://www.wpwhitesecurity.com/blog/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">WP White Security Blog</a>
72
+ &nbsp;&nbsp;&nbsp;&nbsp;
73
+ <a class="button" href="http://www.wpwhitesecurity.com/wordpress-security/?utm_source=wpsalabt&utm_medium=txtlink&utm_campaign=wpsal" target="_blank">WordPress Security Category</a>
74
+ </div>
75
+ </div>
76
+ </div>
77
+
78
+ <div style="position: absolute; right: 70px; width: 180px; top: 10px;">
79
+ <div class="postbox">
80
+ <h3 class="hndl"><span>WP Password Policy Manager</span></h3>
81
+ <div class="inside">
82
+ <p>
83
+ Easily configure WordPress password policies and ensure users use strong passwords with our plugin WP Password Policy Manager.
84
+ </p>
85
+ <a class="button button-primary" href="http://wordpress.org/plugins/wp-password-policy-manager/" target="_blank">Download</a>
86
+ </div>
87
+ </div>
88
+ <div class="postbox">
89
+ <h3 class="hndl"><span>WP Security Audit Log in your Language!</span></h3>
90
+ <div class="inside">
91
+ If you are interested in translating our plugin please drop us an email on
92
+ <a href="mailto:plugins@wpwhitesecurity.com">plugins@wpwhitesecurity.com</a>.
93
+ </div>
94
+ </div>
95
+ </div>
96
+
97
+ </div><?php
98
+ }
99
+
100
+ }
classes/Views/Sandbox.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Views_Sandbox extends WSAL_AbstractView {
4
+
5
+ public function __construct(WpSecurityAuditLog $plugin) {
6
+ parent::__construct($plugin);
7
+ if(is_admin())add_action('wp_ajax_AjaxExecute', array($this, 'AjaxExecute'));
8
+ }
9
+
10
+ public function GetTitle() {
11
+ return 'Sandbox';
12
+ }
13
+
14
+ public function GetIcon() {
15
+ return 'dashicons-admin-generic';
16
+ }
17
+
18
+ public function GetName() {
19
+ return 'Sandbox';
20
+ }
21
+
22
+ public function GetWeight() {
23
+ return 5;
24
+ }
25
+
26
+ public function IsVisible(){
27
+ return $this->_plugin->settings->CurrentUserCan('edit')
28
+ && $this->_plugin->settings->IsSandboxPageEnabled();
29
+ }
30
+
31
+ protected $exec_data = array();
32
+ protected $exec_info = array();
33
+
34
+ protected $snippets = array(
35
+ '' => '',
36
+ 'Current WP User' => 'return wp_get_current_user();',
37
+ );
38
+
39
+ public function HandleError($code, $message, $filename = 'unknown', $lineno = 0){
40
+ if(!isset($this->exec_data['Errors']))
41
+ $this->exec_data['Errors'] = array();
42
+ $this->exec_data['Errors'] = new ErrorException($message, $code, 0, $filename, $lineno);
43
+ return true;
44
+ }
45
+
46
+ protected function BeforeExecute(){
47
+ $this->exec_info['_Time'] = microtime(true);
48
+ $this->exec_info['_Mem'] = memory_get_usage();
49
+ }
50
+
51
+ protected function AfterExecute(){
52
+ $this->exec_info['Time'] = microtime(true) - $this->exec_info['_Time'];
53
+ $this->exec_info['Mem'] = memory_get_usage() - $this->exec_info['_Mem'];
54
+ if($this->exec_info['Time'] < 0.001){
55
+ $this->exec_info['Time'] = number_format($this->exec_info['Time'] * 1000, 3) . 'ms';
56
+ }else{
57
+ $this->exec_info['Time'] = number_format($this->exec_info['Time'], 3) . 's';
58
+ }
59
+ $this->exec_info['Mem'] = size_format($this->exec_info['Mem']);
60
+ $this->exec_info['Queries'] = get_num_queries();
61
+ }
62
+
63
+ protected function Execute($code){
64
+ try {
65
+ error_reporting(-1);
66
+ ini_set('display_errors', false);
67
+ if(function_exists('xdebug_disable'))xdebug_disable();
68
+
69
+ set_error_handler(array($this, 'HandleError'));
70
+
71
+ ob_start();
72
+
73
+ $this->BeforeExecute();
74
+ $this->exec_data['Result'] = eval($code);
75
+ $this->AfterExecute();
76
+
77
+ $this->exec_data['Output'] = ob_get_clean();
78
+
79
+ if(!$this->exec_data['Output'])
80
+ unset($this->exec_data['Output']);
81
+
82
+ restore_error_handler();
83
+ } catch (Exception $ex) {
84
+ if(!isset($this->exec_data['Errors']))
85
+ $this->exec_data['Errors'] = array();
86
+ $this->exec_data['Errors'][] = $ex;
87
+ }
88
+ }
89
+
90
+ public function AjaxExecuteResponse(){
91
+ echo '<!DOCTYPE html><html><head>';
92
+ echo '<link rel="stylesheet" id="open-sans-css" href="' . $this->_plugin->GetBaseUrl() . '/css/nice_r.css" type="text/css" media="all">';
93
+ echo '<script type="text/javascript" src="'.$this->_plugin->GetBaseUrl() . '/js/nice_r.js"></script>';
94
+ echo '<style type="text/css">';
95
+ echo 'html, body { margin: 0; padding: 0; }';
96
+ echo '.nice_r { position: absolute; padding: 8px; }';
97
+ echo '.nice_r a { overflow: visible; }';
98
+ echo '.faerror { font: 14px Arial; background: #FCC; text-align: center; padding: 32px; }';
99
+ echo '</style>';
100
+ echo '</head><body>';
101
+
102
+ if(($e = error_get_last()) && !isset($this->exec_data['Errors']) && !count($this->exec_data['Errors']))
103
+ $this->HandleError($e['type'], $e['message'], $e['file'], $e['line']);
104
+
105
+ if(count($this->exec_data)){
106
+ $result = new WSAL_Nicer($this->exec_data);
107
+ $result->render();
108
+ }else echo '<div class="faerror">FATAL ERROR</div>';
109
+
110
+ if(count($this->exec_info)){
111
+ ?><script type="text/javascript">
112
+ window.parent.SandboxUpdateState(<?php
113
+ $res = array();
114
+ foreach($this->exec_info as $key => $val)
115
+ if($key && $key[0] != '_')
116
+ $res[$key] = $val;
117
+ echo json_encode($res);
118
+ ?>);
119
+ </script><?php
120
+ }
121
+
122
+ echo '</body></html>';
123
+ }
124
+
125
+ public function AjaxExecute(){
126
+ if(!$this->_plugin->settings->IsSandboxPageEnabled())
127
+ die('Sandbox Disabled.');
128
+ if(!$this->_plugin->settings->CurrentUserCan('edit'))
129
+ die('Access Denied.');
130
+ if(!isset($_REQUEST['code']))
131
+ die('Code parameter expected.');
132
+
133
+ register_shutdown_function(array($this, 'AjaxExecuteResponse'));
134
+ $this->Execute(stripslashes_deep($_REQUEST['code']));
135
+ die;
136
+ }
137
+
138
+ public function Render(){
139
+ $snpt = isset($_REQUEST['snippet']) ? $_REQUEST['snippet'] : '';
140
+ $code = isset($this->snippets[$snpt]) ? $this->snippets[$snpt] : '';
141
+ ?><form id="sandbox" method="post" target="execframe" action="<?php echo admin_url('admin-ajax.php'); ?>">
142
+ <input type="hidden" name="action" value="AjaxExecute" />
143
+ <div id="sandbox-wrap-wrap">
144
+ <div id="sandbox-wrap">
145
+ <textarea name="code" id="sandbox-code"><?php echo esc_html($code); ?></textarea>
146
+ <iframe id="sandbox-result" name="execframe"></iframe>
147
+ </div>
148
+ <div id="sandbox-status">Ready.</div>
149
+ </div>
150
+ <label for="sandbox-snippet" style="float: left; line-height: 26px; display: inline-block; margin-right: 32px; border-right: 1px dotted #CCC; padding-right: 32px;">
151
+ Use Snippet:
152
+ <?php $code = json_encode(admin_url('admin.php?page=wsal-sandbox') . '&snippet='); ?>
153
+ <select id="sandbox-snippet" onchange="location = <?php echo esc_attr($code); ?> + encodeURIComponent(this.value);"><?php
154
+ foreach(array_keys($this->snippets) as $name){
155
+ ?><option value="<?php echo esc_attr($name); ?>"<?php if($name == $snpt)echo ' selected="selected"'; ?>><?php _e($name); ?></option><?php
156
+ }
157
+ ?></select>
158
+ </label>
159
+ <input type="submit" name="submit" id="sandbox-submit" class="button button-primary" value="Execute">
160
+ <img id="sandbox-loader" style="margin: 6px 12px; display: none;" src="http://cdnjs.cloudflare.com/ajax/libs/jstree/3.0.0-beta10/themes/default/throbber.gif" width="16" height="16" alt="Loading..."/>
161
+ </form><?php
162
+ }
163
+
164
+ public function Header(){
165
+ ?><link rel="stylesheet" href="//cdn.jsdelivr.net/codemirror/4.0.3/codemirror.css">
166
+ <style type="text/css">
167
+ #sandbox-wrap-wrap {
168
+ resize: vertical; height: 400px; overflow: auto; margin: 16px 0; padding-bottom: 16px; position: relative; border: 1px solid #DDD;
169
+ }
170
+ #sandbox-wrap {
171
+ overflow: hidden; height: 100% !important; position: relative; box-sizing: border-box;
172
+ }
173
+ #sandbox-wrap textarea,
174
+ #sandbox-wrap .CodeMirror {
175
+ resize: none; width: 50%; height: 100%; border-bottom: 1px solid #ddd; font: 12px Consolas; box-sizing: border-box;
176
+ }
177
+ #sandbox-wrap iframe {
178
+ resize: none; width: 50%; height: 100%; border-bottom: 1px solid #ddd; background: #FFF; position: absolute; top: 0; right: 0; box-sizing: border-box; border-left: 4px solid #DDD;
179
+ }
180
+ #sandbox-status {
181
+ font: 10px Tahoma; padding: 2px; position: absolute; left: 0; right: 16px; bottom: 0;
182
+ }
183
+ #sandbox-status ul {
184
+ list-style: none; margin: 0;
185
+ }
186
+ #sandbox-status li {
187
+ float: left; padding-right: 4px; border-right: 1px solid #CCC; margin: 0 4px 0 0;
188
+ }
189
+ </style><?php
190
+ }
191
+
192
+ public function Footer(){
193
+ ?><script src="//cdn.jsdelivr.net/codemirror/4.0.3/codemirror.js"></script>
194
+ <script src="//cdn.jsdelivr.net/codemirror/4.0.3/addon/edit/matchbrackets.js"></script>
195
+ <script src="//cdn.jsdelivr.net/codemirror/4.0.3/mode/clike/clike.js"></script>
196
+ <script src="//cdn.jsdelivr.net/codemirror/4.0.3/mode/php/php.js"></script>
197
+ <script type="text/javascript">
198
+ jQuery(document).ready(function(){
199
+ var ed = CodeMirror.fromTextArea(
200
+ jQuery('#sandbox-code')[0],
201
+ {
202
+ lineNumbers: true,
203
+ matchBrackets: true,
204
+ mode: "text/x-php",
205
+ indentUnit: 4,
206
+ indentWithTabs: true
207
+ }
208
+ );
209
+
210
+ jQuery('#sandbox').submit(function(){
211
+ if(!ed.isClean())jQuery('#sandbox-snippet').val('');
212
+ jQuery('#sandbox-loader').show();
213
+ });
214
+
215
+ jQuery('#sandbox-result').on('load error', function(){
216
+ jQuery('#sandbox-loader').hide();
217
+ });
218
+
219
+ //jQuery('#sandbox').submit();
220
+ });
221
+
222
+ function SandboxUpdateState(data){
223
+ var ul = jQuery('<ul/>');
224
+ for(var key in data)
225
+ ul.append(jQuery('<li/>').text(key + ': ' + data[key]));
226
+ jQuery('#sandbox-status').html('').append(ul);
227
+ }
228
+ </script><?php
229
+ }
230
+
231
+ }
classes/Views/Settings.php ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Views_Settings extends WSAL_AbstractView {
4
+
5
+ public function __construct(WpSecurityAuditLog $plugin) {
6
+ parent::__construct($plugin);
7
+ add_action('wp_ajax_AjaxCheckSecurityToken', array($this, 'AjaxCheckSecurityToken'));
8
+ }
9
+
10
+ public function HasPluginShortcutLink(){
11
+ return true;
12
+ }
13
+
14
+ public function GetTitle() {
15
+ return 'Settings';
16
+ }
17
+
18
+ public function GetIcon() {
19
+ return 'dashicons-admin-generic';
20
+ }
21
+
22
+ public function GetName() {
23
+ return 'Settings';
24
+ }
25
+
26
+ public function GetWeight() {
27
+ return 3;
28
+ }
29
+
30
+ protected function GetTokenType($token){
31
+ $users = array();
32
+ foreach(get_users('blog_id=0&fields[]=user_login') as $obj)
33
+ $users[] = $obj->user_login;
34
+ $roles = array_keys(get_editable_roles());
35
+
36
+ if(in_array($token, $users))return 'user';
37
+ if(in_array($token, $roles))return 'role';
38
+ return 'other';
39
+ }
40
+
41
+ protected function Save(){
42
+ $this->_plugin->settings->SetPruningDate($_REQUEST['PruningDate']);
43
+ $this->_plugin->settings->SetPruningLimit($_REQUEST['PruningLimit']);
44
+ $this->_plugin->settings->SetWidgetsEnabled($_REQUEST['EnableDashboardWidgets']);
45
+ $this->_plugin->settings->SetAllowedPluginViewers(isset($_REQUEST['Viewers']) ? $_REQUEST['Viewers'] : array());
46
+ $this->_plugin->settings->SetAllowedPluginEditors(isset($_REQUEST['Editors']) ? $_REQUEST['Editors'] : array());
47
+ $this->_plugin->settings->SetRefreshAlertsEnabled($_REQUEST['EnableAuditViewRefresh']);
48
+ $this->_plugin->settings->ClearDevOptions();
49
+ if(isset($_REQUEST['DevOptions']))
50
+ foreach($_REQUEST['DevOptions'] as $opt)
51
+ $this->_plugin->settings->SetDevOptionEnabled($opt, true);
52
+ }
53
+
54
+ public function AjaxCheckSecurityToken(){
55
+ if(!$this->_plugin->settings->CurrentUserCan('view'))
56
+ die('Access Denied.');
57
+ if(!isset($_REQUEST['token']))
58
+ die('Token parameter expected.');
59
+ die($this->GetTokenType($_REQUEST['token']));
60
+ }
61
+
62
+ public function Render(){
63
+ if(!$this->_plugin->settings->CurrentUserCan('edit')){
64
+ wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
65
+ }
66
+ if(isset($_POST['submit'])){
67
+ try {
68
+ $this->Save();
69
+ ?><div class="updated"><p><?php _e('Settings have been saved.'); ?></p></div><?php
70
+ }catch(Exception $ex){
71
+ ?><div class="error"><p><?php _e('Error: '); ?><?php echo $ex->getMessage(); ?></p></div><?php
72
+ }
73
+ }
74
+ ?><form id="audit-log-settings" method="post">
75
+ <input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
76
+ <input type="hidden" id="ajaxurl" value="<?php echo esc_attr(admin_url('admin-ajax.php')); ?>" />
77
+
78
+ <table class="form-table">
79
+ <tbody>
80
+ <tr>
81
+ <th><label for="delete1"><?php _e('Security Alerts Pruning'); ?></label></th>
82
+ <td>
83
+ <fieldset>
84
+ <?php $text = __('(eg: 1 month)'); ?>
85
+ <!--<input type="radio" id="delete1" style="margin-top: 2px;"/>-->
86
+ <label for="delete1"><?php echo __('Delete alerts older than'); ?></label>
87
+ <input type="text" name="PruningDate" placeholder="<?php echo $text; ?>"
88
+ value="<?php echo esc_attr($this->_plugin->settings->GetPruningDate()); ?>"/>
89
+ <span> <?php echo $text; ?></span>
90
+ </fieldset>
91
+ </td>
92
+ </tr>
93
+ <tr>
94
+ <th></th>
95
+ <td>
96
+ <fieldset>
97
+ <?php $max = $this->_plugin->settings->GetMaxAllowedAlerts(); ?>
98
+ <?php $text = sprintf(__('(1 to %d alerts)'), $max); ?>
99
+ <!--<input type="radio" id="delete2" style="margin-top: 2px;"/>-->
100
+ <label for="delete2"><?php echo __('Keep up to'); ?></label>
101
+ <input type="text" name="PruningLimit" placeholder="<?php echo $text;?>"
102
+ value="<?php echo esc_attr($this->_plugin->settings->GetPruningLimit()); ?>"/>
103
+ <span><?php echo $text; ?></span>
104
+ <p class="description"><?php
105
+ echo sprintf(__('By default we keep up to %d WordPress Security Events.'), $max);
106
+ ?></p>
107
+ </fieldset>
108
+ </td>
109
+ </tr>
110
+ <tr>
111
+ <th><label for="dwoption_on"><?php _e('Alerts Dashboard Widget'); ?></label></th>
112
+ <td>
113
+ <fieldset>
114
+ <?php $dwe = $this->_plugin->settings->IsWidgetsEnabled(); ?>
115
+ <label for="dwoption_on">
116
+ <input type="radio" name="EnableDashboardWidgets" id="dwoption_on" style="margin-top: 2px;" <?php if($dwe)echo 'checked="checked"'; ?> value="1">
117
+ <span><?php _e('On'); ?></span>
118
+ </label>
119
+ <br/>
120
+ <label for="dwoption_off">
121
+ <input type="radio" name="EnableDashboardWidgets" id="dwoption_off" style="margin-top: 2px;" <?php if(!$dwe)echo 'checked="checked"'; ?> value="0">
122
+ <span><?php _e('Off'); ?></span>
123
+ </label>
124
+ <br/>
125
+ <p class="description"><?php
126
+ echo sprintf(
127
+ __('Display a dashboard widget with the latest %d security alerts.'),
128
+ $this->_plugin->settings->GetDashboardWidgetMaxAlerts()
129
+ );
130
+ ?></p>
131
+ </fieldset>
132
+ </td>
133
+ </tr>
134
+ <tr>
135
+ <th><label for="ViewerQueryBox"><?php _e('Can View Alerts'); ?></label></th>
136
+ <td>
137
+ <fieldset>
138
+ <input type="text" id="ViewerQueryBox" style="float: left; display: block; width: 250px;">
139
+ <input type="button" id="ViewerQueryAdd" style="float: left; display: block;" class="button-primary" value="Add">
140
+ <br style="clear: both;"/>
141
+ <p class="description"><?php
142
+ _e('Users and Roles in this list can view the security alerts');
143
+ ?></p>
144
+ <div id="ViewerList"><?php
145
+ foreach($this->_plugin->settings->GetAllowedPluginViewers() as $item){
146
+ ?><span class="sectoken-<?php echo $this->GetTokenType($item); ?>">
147
+ <input type="hidden" name="Viewers[]" value="<?php echo esc_attr($item); ?>"/>
148
+ <?php echo esc_html($item); ?>
149
+ <a href="javascript:;" title="Remove">&times;</a>
150
+ </span><?php
151
+ }
152
+ ?></div>
153
+ </fieldset>
154
+ </td>
155
+ </tr>
156
+ <tr>
157
+ <th><label for="EditorQueryBox"><?php _e('Can Manage Plugin'); ?></label></th>
158
+ <td>
159
+ <fieldset>
160
+ <input type="text" id="EditorQueryBox" style="float: left; display: block; width: 250px;">
161
+ <input type="button" id="EditorQueryAdd" style="float: left; display: block;" class="button-primary" value="Add">
162
+ <br style="clear: both;"/>
163
+ <p class="description"><?php
164
+ _e('Users and Roles in this list can manage the plugin settings');
165
+ ?></p>
166
+ <div id="EditorList"><?php
167
+ foreach($this->_plugin->settings->GetAllowedPluginEditors() as $item){
168
+ ?><span class="sectoken-<?php echo $this->GetTokenType($item); ?>">
169
+ <input type="hidden" name="Editors[]" value="<?php echo esc_attr($item); ?>"/>
170
+ <?php echo esc_html($item); ?>
171
+ <a href="javascript:;" title="Remove">&times;</a>
172
+ </span><?php
173
+ }
174
+ ?></div>
175
+ </fieldset>
176
+ </td>
177
+ </tr>
178
+ <tr>
179
+ <th><label for="aroption_on"><?php _e('Refresh Audit View'); ?></label></th>
180
+ <td>
181
+ <fieldset>
182
+ <?php $are = $this->_plugin->settings->IsRefreshAlertsEnabled(); ?>
183
+ <label for="aroption_on">
184
+ <input type="radio" name="EnableAuditViewRefresh" id="aroption_on" style="margin-top: 2px;" <?php if($are)echo 'checked="checked"'; ?> value="1">
185
+ <span><?php _e('Automatic'); ?></span>
186
+ </label>
187
+ <span class="description"> &mdash; <?php _e('Refresh Audit View as soon as there are new events.'); ?></span>
188
+ <br/>
189
+ <label for="aroption_off">
190
+ <input type="radio" name="EnableAuditViewRefresh" id="aroption_off" style="margin-top: 2px;" <?php if(!$are)echo 'checked="checked"'; ?> value="0">
191
+ <span><?php _e('Manual'); ?></span>
192
+ </label>
193
+ <span class="description"> &mdash; <?php _e('Refresh Audit View only when page is reloaded.'); ?></span>
194
+ <br/>
195
+ </fieldset>
196
+ </td>
197
+ </tr>
198
+ <tr>
199
+ <th><label><?php _e('Developer Options'); ?></label></th>
200
+ <td>
201
+ <fieldset><?php
202
+ foreach(array(
203
+ WSAL_Settings::OPT_DEV_DATA_INSPECTOR => array('Data Inspector', 'View data logged for each triggered alert.'),
204
+ WSAL_Settings::OPT_DEV_PHP_ERRORS => array('PHP Errors', 'Enables sensor for alerts generated from PHP.'),
205
+ WSAL_Settings::OPT_DEV_REQUEST_LOG => array('Request Log', 'Enables logging request to file.'),
206
+ WSAL_Settings::OPT_DEV_SANDBOX_PAGE => array('Sandbox', 'Enables sandbox for testing PHP code.'),
207
+ ) as $opt => $info){
208
+ ?><label for="devoption_<?php echo $opt; ?>">
209
+ <input type="checkbox" name="DevOptions[]" id="devoption_<?php echo $opt; ?>" <?php
210
+ if($this->_plugin->settings->IsDevOptionEnabled($opt))echo 'checked="checked"'; ?> value="<?php echo $opt; ?>">
211
+ <span><?php _e($info[0]); ?></span>
212
+ <?php if(isset($info[1]) && $info[1]){ ?>
213
+ <span class="description"> &mdash; <?php _e($info[1]); ?></span>
214
+ <?php }
215
+ ?></label><br/><?php
216
+ }
217
+ ?></fieldset>
218
+ </td>
219
+ </tr>
220
+ </tbody>
221
+ </table>
222
+
223
+ <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="Save Changes"></p>
224
+ </form><?php
225
+ }
226
+
227
+ public function Header(){
228
+ wp_enqueue_style(
229
+ 'settings',
230
+ $this->_plugin->GetBaseUrl() . '/css/settings.css',
231
+ array(),
232
+ filemtime($this->_plugin->GetBaseDir() . '/css/settings.css')
233
+ );
234
+ }
235
+
236
+ public function Footer() {
237
+ wp_enqueue_script(
238
+ 'settings',
239
+ $this->_plugin->GetBaseUrl() . '/js/settings.js',
240
+ array(),
241
+ filemtime($this->_plugin->GetBaseDir() . '/js/settings.js')
242
+ );
243
+ }
244
+
245
+ }
classes/Views/ToggleAlerts.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_Views_ToggleAlerts extends WSAL_AbstractView {
4
+
5
+ public function GetTitle() {
6
+ return 'Enable/Disable Alerts';
7
+ }
8
+
9
+ public function GetIcon() {
10
+ return 'dashicons-forms';
11
+ }
12
+
13
+ public function GetName() {
14
+ return 'Enable/Disable Alerts';
15
+ }
16
+
17
+ public function GetWeight() {
18
+ return 2;
19
+ }
20
+
21
+ protected function GetSafeCatgName($name){
22
+ return strtolower(
23
+ preg_replace('/[^A-Za-z0-9\-]/', '-', $name)
24
+ );
25
+ }
26
+
27
+ public function Render(){
28
+ if(!$this->_plugin->settings->CurrentUserCan('edit')){
29
+ wp_die( __( 'You do not have sufficient permissions to access this page.' ) );
30
+ }
31
+ $alert = new WSAL_Alert(); // IDE type hinting
32
+ $groupedAlerts = $this->_plugin->alerts->GetCategorizedAlerts();
33
+ $safeNames = array_map(array($this, 'GetSafeCatgName'), array_keys($groupedAlerts));
34
+ $safeNames = array_combine(array_keys($groupedAlerts), $safeNames);
35
+ if(isset($_POST['submit']) && isset($_POST['alert'])){
36
+ try {
37
+ $enabled = array_map('intval', $_POST['alert']);
38
+ $disabled = array();
39
+ foreach($this->_plugin->alerts->GetAlerts() as $alert)
40
+ if(!in_array($alert->type, $enabled))
41
+ $disabled[] = $alert->type;
42
+ $this->_plugin->alerts->SetDisabledAlerts($disabled);
43
+ ?><div class="updated"><p><?php _e('Settings have been saved.'); ?></p></div><?php
44
+ }catch(Exception $ex){
45
+ ?><div class="error"><p><?php _e('Error: '); ?><?php echo $ex->getMessage(); ?></p></div><?php
46
+ }
47
+ }
48
+ ?><h2 id="wsal-tabs" class="nav-tab-wrapper"><?php
49
+ foreach($safeNames as $name => $safe){
50
+ ?><a href="#tab-<?php echo $safe; ?>" class="nav-tab"><?php echo __($name); ?></a><?php
51
+ }
52
+ ?></h2>
53
+ <form id="audit-log-viewer" method="post">
54
+ <input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
55
+ <div class="nav-tabs"><?php
56
+ foreach($groupedAlerts as $name => $alerts){
57
+ $active = array(); $allactive = true;
58
+ foreach($alerts as $alert){
59
+ $active[$alert->type] = $this->_plugin->alerts->IsEnabled($alert->type);
60
+ if(!$active[$alert->type])$allactive = false;
61
+ }
62
+ ?><table class="wp-list-table wsal-tab widefat fixed" cellspacing="0" id="tab-<?php echo $safeNames[$name]; ?>">
63
+ <thead>
64
+ <tr>
65
+ <th width="48"><input type="checkbox"<?php if($allactive)echo 'checked="checked"'; ?>/></th>
66
+ <th width="80">Code</th>
67
+ <th width="100">Type</th>
68
+ <th>Description</th>
69
+ </tr>
70
+ </thead>
71
+ <tbody><?php
72
+ foreach($alerts as $alert){
73
+ $attrs = '';
74
+ switch(true){
75
+ case !$alert->mesg:
76
+ $attrs = ' title="Not Implemented" class="alert-incomplete"';
77
+ break;
78
+ case false:
79
+ $attrs = ' title="Not Available" class="alert-unavailable"';
80
+ break;
81
+ }
82
+ ?><tr<?php echo $attrs; ?>>
83
+ <th><input name="alert[]" type="checkbox" <?php if($active[$alert->type])echo 'checked="checked"'; ?> value="<?php echo (int)$alert->type; ?>"></th>
84
+ <td><?php echo str_pad($alert->type, 4, '0', STR_PAD_LEFT); ?></td>
85
+ <td><?php echo $this->_plugin->constants->GetConstantBy('value', $alert->code)->name; ?></td>
86
+ <td><?php echo esc_html(__($alert->desc)); ?></td>
87
+ </tr><?php
88
+ }
89
+ ?></tbody>
90
+ </table><?php
91
+ }
92
+ ?></div>
93
+ <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="Save Changes"></p>
94
+ </form><?php
95
+ }
96
+
97
+ public function Header() {
98
+ ?><style type="text/css">
99
+ .wsal-tab {
100
+ display: none;
101
+ }
102
+ .wsal-tab tr.alert-incomplete td {
103
+ color: #9BE;
104
+ }
105
+ .wsal-tab tr.alert-unavailable td {
106
+ color: #CCC;
107
+ }
108
+ </style><?php
109
+ }
110
+
111
+ public function Footer() {
112
+ ?><script type="text/javascript">
113
+ jQuery(document).ready(function(){
114
+ // tab handling code
115
+ jQuery('#wsal-tabs>a').click(function(){
116
+ jQuery('#wsal-tabs>a').removeClass('nav-tab-active');
117
+ jQuery('table.wsal-tab').hide();
118
+ jQuery(jQuery(this).addClass('nav-tab-active').attr('href')).show();
119
+ });
120
+ // checkbox handling code
121
+ jQuery('table.wsal-tab>thead>tr>th>:checkbox').change(function(){
122
+ jQuery(this).parents('table:first').find('tbody>tr>th>:checkbox').attr('checked', this.checked);
123
+ });
124
+ jQuery('table.wsal-tab>tbody>tr>th>:checkbox').change(function(){
125
+ var allchecked = jQuery(this).parents('tbody:first').find('th>:checkbox:not(:checked)').length === 0;
126
+ jQuery(this).parents('table:first').find('thead>tr>th:first>:checkbox:first').attr('checked', allchecked);
127
+ });
128
+ // show relevant tab
129
+ var hashlink = jQuery('#wsal-tabs>a[href="' + location.hash + '"]');
130
+ if(hashlink.length){
131
+ hashlink.click();
132
+ }else{
133
+ jQuery('#wsal-tabs>a:first').click();
134
+ }
135
+ });
136
+ </script><?php
137
+ }
138
+
139
+ }
classes/WidgetManager.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_WidgetManager {
4
+ /**
5
+ * @var WpSecurityAuditLog
6
+ */
7
+ protected $_plugin;
8
+
9
+ public function __construct(WpSecurityAuditLog $plugin){
10
+ $this->_plugin = $plugin;
11
+ add_action('wp_dashboard_setup', array($this, 'AddWidgets'));
12
+ }
13
+
14
+ public function AddWidgets(){
15
+ if($this->_plugin->settings->IsWidgetsEnabled()
16
+ && $this->_plugin->settings->CurrentUserCan('view')){
17
+ wp_add_dashboard_widget(
18
+ 'wsal',
19
+ __('Latest Alerts') . ' | WP Security Audit Log',
20
+ array($this, 'RenderWidget')
21
+ );
22
+ }
23
+ }
24
+
25
+ public function RenderWidget(){
26
+ $results = WSAL_DB_Occurrence::LoadMulti(
27
+ ' 1 ORDER BY created_on DESC LIMIT '
28
+ . $this->_plugin->settings->GetDashboardWidgetMaxAlerts()
29
+ );
30
+ ?><div><?php
31
+ if(!count($results)){
32
+ ?><p><?php _e('No alerts found.'); ?></p><?php
33
+ }else{
34
+ ?><table class="wp-list-table widefat" cellspacing="0" cellpadding="0">
35
+ <thead>
36
+ <th class="manage-column" style="width: 15%;" scope="col"><?php _e('User'); ?></th>
37
+ <th class="manage-column" style="width: 85%;" scope="col"><?php _e('Description'); ?></th>
38
+ </thead>
39
+ <tbody><?php
40
+ $url = 'admin.php?page=' . $this->_plugin->views->views[0]->GetSafeViewName();
41
+ $fmt = array(new WSAL_Views_AuditLogList_Internal($this->_plugin), 'meta_formatter');
42
+ foreach($results as $entry){
43
+ ?><tr>
44
+ <td><?php
45
+ echo ($un = $entry->GetUsername()) ? esc_html($un) : '<i>unknown</i>';
46
+ ?></td>
47
+ <td>
48
+ <a href="<?php echo $url . '#Event' . $entry->id; ?>"><?php
49
+ echo $entry->GetMessage($fmt);
50
+ ?></a>
51
+ </td>
52
+ </tr><?php
53
+ }
54
+ ?></tbody>
55
+ </table><?php
56
+ }
57
+ ?></div><?php
58
+ }
59
+ }
css/auditlog.css ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wsal-ipp,
2
+ .wsal-ssa {
3
+ display: inline-block;
4
+ color: #555;
5
+ line-height: 30px;
6
+ font-size: 12px;
7
+ }
8
+
9
+ .wsal-ipp select {
10
+ margin-bottom: 6px;
11
+ width: 56px;
12
+ }
13
+
14
+ .wsal-ssa select {
15
+ margin-bottom: 6px;
16
+ width: 120px;
17
+ margin-left: 16px;
18
+ }
19
+
20
+ .column-read,
21
+ .column-type,
22
+ .column-code,
23
+ .column-more {
24
+ width: 70px;
25
+ }
26
+
27
+ .column-crtd {
28
+ width: 96px;
29
+ }
30
+
31
+ .column-user {
32
+ width: 110px;
33
+ }
34
+
35
+ .column-scip {
36
+ width: 120px;
37
+ }
38
+
39
+ .more-info {
40
+ float: right;
41
+ display: block;
42
+ width: 16px;
43
+ height: 16px;
44
+ color: #FFF;
45
+ font-weight: bold;
46
+ font-size: 13px;
47
+ line-height: 10px;
48
+ text-align: center;
49
+ border-radius: 32px;
50
+ margin-left: 8px;
51
+ background: #AAA;
52
+ }
53
+
54
+ .more-info:hover {
55
+ color: #FFF;
56
+ background: #333;
57
+ }
58
+
59
+ .log-read:after {
60
+ display: block;
61
+ width: 16px;
62
+ height: 16px;
63
+ color: #FFF;
64
+ font-weight: bold;
65
+ font-size: 13px;
66
+ line-height: 16px;
67
+ text-align: center;
68
+ border-radius: 32px;
69
+ margin-left: 8px;
70
+ content: "\2713";
71
+ background: #CCC;
72
+ }
73
+
74
+ .log-read-new:after {
75
+ background: #290;
76
+ }
77
+
78
+ .log-type {
79
+ cursor: help;
80
+ }
81
+
82
+ .log-type:after {
83
+ display: block;
84
+ width: 16px;
85
+ height: 16px;
86
+ color: #FFF;
87
+ font-weight: bold;
88
+ font-size: 11px;
89
+ line-height: 17px;
90
+ background: #CCC;
91
+ text-align: center;
92
+ border-radius: 32px;
93
+ margin-left: 8px;
94
+ }
95
+
96
+ /* errors */
97
+ .log-type-0:after,
98
+ .log-type-1:after,
99
+ .log-type-4:after,
100
+ .log-type-16:after,
101
+ .log-type-64:after,
102
+ .log-type-256:after,
103
+ .log-type-4096:after,
104
+ .log-type-E_CRITICAL:after
105
+ { background: #E00; content: "!"; }
106
+
107
+ /* warnings */
108
+ .log-type-2:after,
109
+ .log-type-32:after,
110
+ .log-type-128:after,
111
+ .log-type-512:after
112
+ { background: #EB0; content: "!"; }
113
+
114
+ /* notices/strict/deprecation */
115
+ .log-type-8:after,
116
+ .log-type-1024:after,
117
+ .log-type-2048:after,
118
+ .log-type-8192:after,
119
+ .log-type-16384:after,
120
+ .log-type-E_DEBUG:after
121
+ { background: #09E; content: "i"; }
css/nice_r.css ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .nice_r {
2
+ font: 12px Consolas, "Lucida Console", monospace;
3
+ }
4
+
5
+ .nice_r a {
6
+ display: block;
7
+ text-decoration: none;
8
+ color: #222;
9
+ padding: 2px;
10
+ white-space: nowrap;
11
+ overflow: hidden;
12
+ text-overflow: ellipsis;
13
+ }
14
+
15
+ .nice_r a:hover .nice_r_k,
16
+ .nice_r a:hover .nice_r_d,
17
+ .nice_r a:hover .nice_r_d span,
18
+ .nice_r a:hover .nice_r_p,
19
+ .nice_r a:hover .nice_r_a,
20
+ .nice_r a:hover {
21
+ background-color: Highlight;
22
+ color: HighlightText;
23
+ }
24
+
25
+ .nice_r_a {
26
+ color: #000;
27
+ }
28
+
29
+ .nice_r_ad {
30
+ opacity: 0.5;
31
+ }
32
+
33
+ .nice_r_k {
34
+ color: #060;
35
+ font-weight: bold;
36
+ }
37
+
38
+ .nice_r_d {
39
+ font-size: 11px;
40
+ color: #777;
41
+ }
42
+
43
+ .nice_r_d span {
44
+ color: #333;
45
+ }
46
+
47
+ .nice_r_p {
48
+ color: #000;
49
+ font-weight: bold;
50
+ }
51
+
52
+ .nice_r_v {
53
+ margin-left: 6px;
54
+ padding-left: 6px;
55
+ border-left: 1px dotted #CCC;
56
+ display: none;
57
+ }
58
+
59
+ .nice_r_ir {
60
+ font-style: italic;
61
+ }
62
+
63
+ .nice_r_p.nice_r_t_integer,
64
+ .nice_r_p.nice_r_t_double {
65
+ color: #F0E;
66
+ }
67
+
68
+ .nice_r_p.nice_r_t_string {
69
+ color: #E00;
70
+ }
71
+
72
+ .nice_r_p.nice_r_t_boolean {
73
+ color: #00E;
74
+ }
css/settings.css ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .sectoken-user,
2
+ .sectoken-role,
3
+ .sectoken-other {
4
+ display: inline-block;
5
+ border-width: 1px;
6
+ border-style: solid;
7
+ padding: 2px 4px;
8
+ margin: 2px 0 0 2px;
9
+ border-radius: 3px;
10
+ cursor: default;
11
+ }
12
+
13
+ .sectoken-user a,
14
+ .sectoken-role a,
15
+ .sectoken-other a {
16
+ text-decoration: none;
17
+ font-size: 12px;
18
+ font-weight: bold;
19
+ color: #FFF;
20
+ margin-left: 2px;
21
+ background: #BBB;
22
+ border-radius: 25px;
23
+ height: 14px;
24
+ display: inline-block;
25
+ width: 14px;
26
+ text-align: center;
27
+ line-height: 16px;
28
+ }
29
+
30
+ .sectoken-user a:hover,
31
+ .sectoken-role a:hover,
32
+ .sectoken-other a:hover {
33
+ background: #FB9;
34
+ }
35
+
36
+ .sectoken-user { background: #EFF; border-color: #5BE; }
37
+ .sectoken-role { background: #EFE; border-color: #5B5; }
38
+ .sectoken-other { background: #FFE; border-color: #ED5; }
39
+ .sectoken-del { background: #FEE; border-color: #EBB; }
defaults.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // if not included correctly...
4
+ if ( !class_exists( 'WpSecurityAuditLog' ) ) exit();
5
+
6
+ defined('E_CRITICAL') || define('E_CRITICAL', 'E_CRITICAL');
7
+ defined('E_DEBUG') || define('E_DEBUG', 'E_DEBUG');
8
+
9
+ WpSecurityAuditLog::GetInstance()
10
+ ->constants->UseConstants(array(
11
+ // default PHP constants
12
+ array('name' => 'E_ERROR', 'description' => 'Fatal run-time error.'),
13
+ array('name' => 'E_WARNING', 'description' => 'Run-time warning (non-fatal error).'),
14
+ array('name' => 'E_PARSE', 'description' => 'Compile-time parse error.'),
15
+ array('name' => 'E_NOTICE', 'description' => 'Run-time notice.'),
16
+ array('name' => 'E_CORE_ERROR', 'description' => 'Fatal error that occurred during startup.'),
17
+ array('name' => 'E_CORE_WARNING', 'description' => 'Warnings that occurred during startup.'),
18
+ array('name' => 'E_COMPILE_ERROR', 'description' => 'Fatal compile-time error.'),
19
+ array('name' => 'E_COMPILE_WARNING', 'description' => 'Compile-time warning.'),
20
+ array('name' => 'E_USER_ERROR', 'description' => 'User-generated error message.'),
21
+ array('name' => 'E_USER_WARNING', 'description' => 'User-generated warning message.'),
22
+ array('name' => 'E_USER_NOTICE', 'description' => 'User-generated notice message. '),
23
+ array('name' => 'E_STRICT', 'description' => 'Non-standard/optimal code warning.'),
24
+ array('name' => 'E_RECOVERABLE_ERROR', 'description' => 'Catchable fatal error.'),
25
+ array('name' => 'E_DEPRECATED', 'description' => 'Run-time deprecation notices.'),
26
+ array('name' => 'E_USER_DEPRECATED', 'description' => 'Run-time user deprecation notices.'),
27
+ // custom constants
28
+ array('name' => 'E_CRITICAL', 'description' => 'Critical, high-impact messages.'),
29
+ array('name' => 'E_DEBUG', 'description' => 'Debug informational messages.'),
30
+ ));
31
+
32
+ WpSecurityAuditLog::GetInstance()
33
+ ->alerts->RegisterGroup(array(
34
+ 'Other User Activity' => array(
35
+ array(1000, E_NOTICE, 'User logs in', 'Successfully logged in'),
36
+ array(1001, E_NOTICE, 'User logs out', 'Successfully logged out'),
37
+ array(1002, E_WARNING, 'Login failed', '%Attempts% failed login(s) detected'),
38
+ array(2010, E_NOTICE, 'User uploaded file from Uploads directory', 'Uploaded the file %FileName% in %FilePath%'),
39
+ array(2011, E_WARNING, 'User deleted file from Uploads directory', 'Deleted the file %FileName% from %FilePath%'),
40
+ array(2046, E_CRITICAL, 'User changed a file using the editor', 'Modified %File% with the Theme Editor'),
41
+ ),
42
+ 'Blog Posts' => array(
43
+ array(2000, E_NOTICE, 'User created a new blog post and saved it as draft', 'Created a new blog post called %PostTitle%. Blog post ID is %PostID%'),
44
+ array(2001, E_NOTICE, 'User published a blog post', 'Published a blog post called %PostTitle%. Blog post URL is %PostUrl%'),
45
+ array(2002, E_NOTICE, 'User modified a published blog post', 'Modified the published blog post %PostTitle%. Blog post URL is %PostUrl%'),
46
+ array(2003, E_NOTICE, 'User modified a draft blog post', 'Modified the draft blog post %PostTitle%. Blog post ID is %PostID%'),
47
+ array(2008, E_NOTICE, 'User permanently deleted a blog post from the trash', 'Deleted the post %PostTitle%. Blog post ID is %PostID%'),
48
+ array(2012, E_WARNING, 'User moved a blog post to the trash', 'Moved the blog post %PostTitle% to trash'),
49
+ array(2014, E_CRITICAL, 'User restored a blog post from trash', 'Restored post %PostTitle% from trash'),
50
+ array(2016, E_NOTICE, 'User changed blog post category', 'Changed the category of the post %PostTitle% from %OldCategories% to %NewCategories%'),
51
+ array(2017, E_NOTICE, 'User changed blog post URL', 'Changed the URL of the post %PostTitle% from %OldUrl% to %NewUrl%'),
52
+ array(2019, E_NOTICE, 'User changed blog post author', 'Changed the author of %PostTitle% post from %OldAuthor% to %NewAuthor%'),
53
+ array(2021, E_NOTICE, 'User changed blog post status', 'Changed the status of %PostTitle% post from %OldStatus% to %NewStatus%'),
54
+ array(2023, E_NOTICE, 'User created new category', 'Created a new category called %CategoryName%'),
55
+ array(2024, E_WARNING, 'User deleted category', 'Deleted the %CategoryName% category'),
56
+ array(2025, E_WARNING, 'User changed the visibility of a blog post', 'Changed the visibility of %PostTitle% blog post from %OldVisibility% to %NewVisibility%'),
57
+ array(2027, E_NOTICE, 'User changed the date of a blog post', 'Changed the date of %PostTitle% blog post from %OldDate% to %NewDate%'),
58
+ array(2049, E_NOTICE, 'User sets a post as sticky', 'Set the post %PostTitle% as Sticky'),
59
+ array(2050, E_NOTICE, 'User removes post from sticky', 'Removed the post %PostTitle% from Sticky'),
60
+ ),
61
+ 'Pages' => array(
62
+ array(2004, E_NOTICE, 'User created a new WordPress page and saved it as draft', 'Created a new page called %PostTitle%. Page ID is %PostID%'),
63
+ array(2005, E_NOTICE, 'User published a WorPress page', 'Published a page called %PostTitle%. Page URL is %PostUrl%'),
64
+ array(2006, E_NOTICE, 'User modified a published WordPress page', 'Modified the published page %PostTitle%. Page URL is %PostUrl%'),
65
+ array(2007, E_NOTICE, 'User modified a draft WordPress page', 'Modified the draft page %PostTitle%. page ID is %PostID%'),
66
+ array(2009, E_NOTICE, 'User permanently deleted a page from the trash', 'Deleted the page %PostTitle%. Page ID is %PostID%'),
67
+ array(2013, E_WARNING, 'User moved WordPress page to the trash', 'Moved the page %PostTitle% to trash'),
68
+ array(2015, E_CRITICAL, 'User restored a WordPress page from trash', 'Restored page %PostTitle% from trash'),
69
+ array(2018, E_NOTICE, 'User changed page URL', 'Changed the URL of the page %PostTitle% from %OldUrl% to %NewUrl%'),
70
+ array(2020, E_NOTICE, 'User changed page author', 'Changed the author of %PostTitle% page from %OldAuthor% to %NewAuthor%'),
71
+ array(2022, E_NOTICE, 'User changed page status', 'Changed the status of %PostTitle% page from %OldStatus% to %NewStatus%'),
72
+ array(2026, E_WARNING, 'User changed the visibility of a page post', 'Changed the visibility of %PostTitle% page from %OldVisibility% to %NewVisibility%'),
73
+ array(2028, E_NOTICE, 'User changed the date of a page post', 'Changed the date of %PostTitle% page from %OldDate% to %NewDate%'),
74
+ array(2047, E_NOTICE, 'User changed the parent of a page', 'Changed the parent of %PostTitle% page from %OldParentName% to %NewParentName%'),
75
+ array(2048, E_CRITICAL, 'User changes the template of a page', 'Changed the template of %PostTitle% page from %OldTemplate% to %NewTemplate%'),
76
+ ),
77
+ 'Custom Posts' => array(
78
+ array(2029, E_NOTICE, 'User created a new post with custom post type and saved it as draft', 'Created a new custom post called %PostTitle% of type %PostType%. Post ID is %PostID%'),
79
+ array(2030, E_NOTICE, 'User published a post with custom post type', 'Published a custom post %PostTitle% of type %PostType%. Post URL is %PostUrl%'),
80
+ array(2031, E_NOTICE, 'User modified a post with custom post type', 'Modified custom post %PostTitle% of type %PostType%. Post URL is %PostUrl%'),
81
+ array(2032, E_NOTICE, 'User modified a draft post with custom post type', 'Modified draft custom post %PostTitle% of type is %PostType%. Post URL is %PostUrl%'),
82
+ array(2033, E_WARNING, 'User permanently deleted post with custom post type', 'Deleted custom post %PostTitle% of type %PostType%'),
83
+ array(2034, E_WARNING, 'User moved post with custom post type to trash', 'Moved custom post %PostTitle% to trash. Post type is %PostType%'),
84
+ array(2035, E_CRITICAL, 'User restored post with custom post type from trash', 'Restored custom post %PostTitle% of type %PostType% from trash'),
85
+ array(2036, E_NOTICE, 'User changed the category of a post with custom post type', 'Changed the category(ies) of custom post %PostTitle% of type %PostType% from %OldCategories% to %NewCategories%'),
86
+ array(2037, E_NOTICE, 'User changed the URL of a post with custom post type', 'Changed the URL of custom post %PostTitle% of type %PostType% from %OldUrl% to %NewUrl%'),
87
+ array(2038, E_NOTICE, 'User changed the author or post with custom post type', 'Changed the author of custom post %PostTitle% of type %PostType% from %OldAuthor% to %NewAuthor%'),
88
+ array(2039, E_NOTICE, 'User changed the status of post with custom post type', 'Changed the status of custom post %PostTitle% of type %PostType% from %OldStatus% to %NewStatus%'),
89
+ array(2040, E_WARNING, 'User changed the visibility of a post with custom post type', 'Changed the visibility of custom post %PostTitle% of type %PostType% from %OldVisibility% to %NewVisibility%'),
90
+ array(2041, E_NOTICE, 'User changed the date of post with custom post type', 'Changed the date of custom post %PostTitle% of type %PostType% from %OldDate% to %NewDate%'),
91
+ ),
92
+ 'Widgets' => array(
93
+ array(2042, E_CRITICAL, 'User added a new widget', 'Added a new %WidgetName% widget in %Sidebar%'),
94
+ array(2043, E_WARNING, 'User modified a widget', 'Modified the %WidgetName% widget in %Sidebar%'),
95
+ array(2044, E_CRITICAL, 'User deleted widget', 'Deleted the %WidgetName% widget from %Sidebar%'),
96
+ array(2045, E_NOTICE, 'User moved widget', 'Moved the %WidgetName% widget from %OldSidebar% to %NewSidebar%'),
97
+ ),
98
+ 'User Profiles' => array(
99
+ array(4000, E_CRITICAL, 'A new user was created on WordPress', 'User %NewUserData->Username% subscribed with a role of %NewUserData->Roles%'),
100
+ array(4001, E_CRITICAL, 'A user created another WordPress user', 'Created a new user %NewUserData->Username% with the role of %NewUserData->Roles%'),
101
+ array(4002, E_CRITICAL, 'The role of a user was changed by another WordPress user', 'Changed the role of user %TargetUsername% from %OldRole% to %NewRole%'),
102
+ array(4003, E_CRITICAL, 'User has changed his or her password', 'Changed the password'),
103
+ array(4004, E_CRITICAL, 'A user changed another user\'s password', 'Changed the password for user %TargetUserData->Username% with the role of %TargetUserData->Roles%'),
104
+ array(4005, E_NOTICE, 'User changed his or her email address', 'Changed the email address from %OldEmail% to %NewEmail%'),
105
+ array(4006, E_NOTICE, 'A user changed another user\'s email address', 'Changed the email address of user account %TargetUsername% from %OldEmail% to %NewEmail%'),
106
+ array(4007, E_CRITICAL, 'A user was deleted by another user', 'Deleted User %TargetUserData->Username% with the role of %TargetUserData->Roles%'),
107
+ ),
108
+ 'Plugins & Themes' => array(
109
+ array(5000, E_CRITICAL, 'User installed a plugin', 'Installed the plugin %NewPlugin->Name% in %NewPlugin->plugin_dir_path%'),
110
+ array(5001, E_CRITICAL, 'User activated a WordPress plugin', 'Activated the plugin %PluginData->Name% installed in %PluginFile%'),
111
+ array(5002, E_CRITICAL, 'User deactivated a WordPress plugin', 'Deactivated the plugin %PluginData->Name% installed in %PluginFile%'),
112
+ array(5003, E_CRITICAL, 'User uninstalled a plugin', 'Uninstalled the plugin %PluginData->Name% which was installed in %PluginFile%'),
113
+ array(5004, E_WARNING, 'User upgraded a plugin', 'Upgraded the plugin %PluginData->Name% installed in %PluginFile%'),
114
+ array(5005, E_CRITICAL, 'User installed a theme', 'Installed theme "%NewTheme->Name%" in %NewTheme->get_template_directory%'),
115
+ array(5006, E_CRITICAL, 'User activated a theme', 'Activated theme "%NewTheme->Name%", installed in %NewTheme->get_template_directory%'),
116
+ ),
117
+ 'System Activity' => array(
118
+ array(0000, E_CRITICAL, 'Unknown Error', 'An unexpected error has occurred'),
119
+ array(0001, E_CRITICAL, 'PHP error', '%Message%'),
120
+ array(0002, E_WARNING, 'PHP warning', '%Message%'),
121
+ array(0003, E_NOTICE, 'PHP notice', '%Message%'),
122
+ array(0004, E_CRITICAL, 'PHP exception', '%Message%'),
123
+ array(0005, E_CRITICAL, 'PHP shutdown error', '%Message%'),
124
+ array(6000, E_NOTICE, 'Events automatically pruned by system', '%EventCount% event(s) automatically deleted by system'),
125
+ array(6001, E_CRITICAL, 'Option Anyone Can Register in WordPress settings changed', '%NewValue% the option "Anyone can register"'),
126
+ array(6002, E_CRITICAL, 'New User Default Role changed', 'Changed the New User Default Role from %OldRole% to %NewRole%'),
127
+ array(6003, E_CRITICAL, 'WordPress Administrator Notification email changed', 'Changed the WordPress administrator notifications email address from %OldEmail% to %NewEmail%'),
128
+ array(6004, E_CRITICAL, 'WordPress was updated', 'Updated WordPress from version %OldVersion% to %NewVersion%'),
129
+ array(6005, E_CRITICAL, 'User changes the WordPress Permalinks', 'Changed the WordPress permalinks from %OldPattern% to %NewPattern%'),
130
+ ),
131
+ 'MultiSite' => array(
132
+ array(4008, E_CRITICAL, 'User granted Super Admin privileges', 'Granted Super Admin privileges to %TargetUsername%'),
133
+ array(4009, E_CRITICAL, 'User revoked from Super Admin privileges', 'Revoked Super Admin privileges from %TargetUsername%'),
134
+ array(4010, E_CRITICAL, 'Existing user added to a site', 'Added existing user %Username% with %UserRole% role to site %SiteName%'),
135
+ array(4011, E_CRITICAL, 'User removed from site', 'Removed user %Username% with role %UserRole% from %SiteName% site'),
136
+ array(4012, E_CRITICAL, 'New network user created', 'Created a new network user %NewUserData->Username%'),
137
+ array(7000, E_CRITICAL, 'New site added on network', 'Added site %SiteName% to the network'),
138
+ array(7001, E_CRITICAL, 'Existing site archived', 'Archived site %SiteName%'),
139
+ array(7002, E_CRITICAL, 'Archived site has been unarchived', 'Unarchived site %SiteName%'),
140
+ array(7003, E_CRITICAL, 'Deactivated site has been activated', 'Activated site %SiteName%'),
141
+ array(7004, E_CRITICAL, 'Site has been deactivated', 'Deactivated site %SiteName%'),
142
+ array(7005, E_CRITICAL, 'Existing site deleted from network', 'Deleted site %SiteName%'),
143
+ ),
144
+ ));
{res/img → img}/logo-main-menu.png RENAMED
File without changes
inc/WPPH.php DELETED
@@ -1,513 +0,0 @@
1
- <?php
2
- /**
3
- * Base class
4
- */
5
- class WPPH
6
- {
7
- /**
8
- * The required user capability to display the menu
9
- * @since v0.5
10
- * @var string
11
- */
12
- static $requiredCapMenu = 'read';
13
- /**
14
- * @since v0.5
15
- * @var string
16
- */
17
- static $baseMenuSlug = WPPH_PLUGIN_PREFIX;
18
-
19
- static function loadTextDomain()
20
- {
21
- load_plugin_textdomain(WPPH_PLUGIN_TEXT_DOMAIN, false, WPPH_PLUGIN_DIR.'languages/');
22
- }
23
-
24
- /**
25
- * @since v0.5
26
- * Retrieve the list of all events to display in the enable/disable alerts page
27
- * @return array
28
- */
29
- static function getDefaultEvents()
30
- {
31
- return array(
32
- 'Other_User_Activity' => array(
33
- 1000 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User logs in',WPPH_PLUGIN_TEXT_DOMAIN)),
34
- 1001 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User Logs out',WPPH_PLUGIN_TEXT_DOMAIN)),
35
- 1002 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('Failed login detected',WPPH_PLUGIN_TEXT_DOMAIN)),
36
- 2010 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User uploaded a file to the uploads directory',WPPH_PLUGIN_TEXT_DOMAIN)),
37
- 2011 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User deleted a file from the uploads directory',WPPH_PLUGIN_TEXT_DOMAIN)),
38
- ),
39
- 'Pages' => array(
40
- 2004 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User created a new WordPress page and saved it as draft',WPPH_PLUGIN_TEXT_DOMAIN)),
41
- 2005 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User published a WorPress page',WPPH_PLUGIN_TEXT_DOMAIN)),
42
- 2006 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User modified a published WordPress page',WPPH_PLUGIN_TEXT_DOMAIN)),
43
- 2007 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User modified a draft WordPress page',WPPH_PLUGIN_TEXT_DOMAIN)),
44
- 2009 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User permanently deleted a page from the trash',WPPH_PLUGIN_TEXT_DOMAIN)),
45
- 2013 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User moved WordPress page to the trash',WPPH_PLUGIN_TEXT_DOMAIN)),
46
- 2015 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User restored a WordPress page from trash',WPPH_PLUGIN_TEXT_DOMAIN)),
47
- 2018 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed page URL',WPPH_PLUGIN_TEXT_DOMAIN)),
48
- 2020 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed page author',WPPH_PLUGIN_TEXT_DOMAIN)),
49
- 2022 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed page status',WPPH_PLUGIN_TEXT_DOMAIN)),
50
- 2026 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User changed the visibility of a page',WPPH_PLUGIN_TEXT_DOMAIN)),
51
- 2028 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed the date of a page post',WPPH_PLUGIN_TEXT_DOMAIN)),
52
- ),
53
- 'Blog_Posts' => array(
54
- 2000 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User created a new blog post and saved it as draft',WPPH_PLUGIN_TEXT_DOMAIN)),
55
- 2001 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User published a blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
56
- 2002 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User modified a published blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
57
- 2003 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User modified a draft blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
58
- 2008 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User permanently deleted a blog post from the trash',WPPH_PLUGIN_TEXT_DOMAIN)),
59
- 2012 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User moved a blog post to the trash',WPPH_PLUGIN_TEXT_DOMAIN)),
60
- 2014 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User restored a blog post from trash',WPPH_PLUGIN_TEXT_DOMAIN)),
61
- 2016 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed blog post category',WPPH_PLUGIN_TEXT_DOMAIN)),
62
- 2017 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed blog post URL',WPPH_PLUGIN_TEXT_DOMAIN)),
63
- 2019 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed blog post author',WPPH_PLUGIN_TEXT_DOMAIN)),
64
- 2021 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed blog post status',WPPH_PLUGIN_TEXT_DOMAIN)),
65
- 2023 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User created new category',WPPH_PLUGIN_TEXT_DOMAIN)),
66
- 2024 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User deleted a category',WPPH_PLUGIN_TEXT_DOMAIN)),
67
- 2025 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User changed the visibility of a blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
68
- 2027 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed the date of a blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
69
- ),
70
- 'Custom_Posts' => array(
71
- 2029 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User created a new custom blog post and saved it as draft',WPPH_PLUGIN_TEXT_DOMAIN)),
72
- 2030 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User published a custom blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
73
- 2031 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User modified a published custom blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
74
- 2032 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User modified a draft custom blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
75
- 2033 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User permanently deleted a custom blog post from the trash',WPPH_PLUGIN_TEXT_DOMAIN)),
76
- 2034 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User moved a custom blog post to the trash',WPPH_PLUGIN_TEXT_DOMAIN)),
77
- 2035 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User restored a custom blog post from trash',WPPH_PLUGIN_TEXT_DOMAIN)),
78
- 2036 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed custom blog post category',WPPH_PLUGIN_TEXT_DOMAIN)),
79
- 2037 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed custom blog post URL',WPPH_PLUGIN_TEXT_DOMAIN)),
80
- 2038 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed custom blog post author',WPPH_PLUGIN_TEXT_DOMAIN)),
81
- 2039 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed custom blog post status',WPPH_PLUGIN_TEXT_DOMAIN)),
82
- 2040 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User changed the visibility of a custom blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
83
- 2041 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User changed the date of a custom blog post',WPPH_PLUGIN_TEXT_DOMAIN)),
84
- ),
85
- 'Users_Profiles' => array(
86
- 4000 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('A new user was created on WordPress',WPPH_PLUGIN_TEXT_DOMAIN)),
87
- 4001 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('A user created another WordPress user',WPPH_PLUGIN_TEXT_DOMAIN)),
88
- 4002 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('The role of a user was changed by another WordPress user',WPPH_PLUGIN_TEXT_DOMAIN)),
89
- 4003 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User has changed his or her password',WPPH_PLUGIN_TEXT_DOMAIN)),
90
- 4004 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('A user changed another user\'s password',WPPH_PLUGIN_TEXT_DOMAIN)),
91
- 4005 => array('type' => WPPH_E_NOTICE_TEXT,'text' => __('User changed his or her email address',WPPH_PLUGIN_TEXT_DOMAIN)),
92
- 4006 => array('type' => WPPH_E_NOTICE_TEXT,'text' => __('A user changed another user\'s email address',WPPH_PLUGIN_TEXT_DOMAIN)),
93
- 4007 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('A user was deleted by another user',WPPH_PLUGIN_TEXT_DOMAIN)),
94
- ),
95
- 'Widgets' => array(
96
- 2042 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User added a new widget',WPPH_PLUGIN_TEXT_DOMAIN)),
97
- 2043 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User modified a widget',WPPH_PLUGIN_TEXT_DOMAIN)),
98
- 2044 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User deleted a widget',WPPH_PLUGIN_TEXT_DOMAIN)),
99
- 2045 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User moved a widget',WPPH_PLUGIN_TEXT_DOMAIN)),
100
- ),
101
- 'Themes' => array(
102
- 3000 => array('type' => WPPH_E_NOTICE_TEXT, 'text' => __('User activated a theme.',WPPH_PLUGIN_TEXT_DOMAIN)),
103
- ),
104
- 'Plugins' => array(
105
- 5000 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User installed a plugin',WPPH_PLUGIN_TEXT_DOMAIN)),
106
- 5001 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User activated a WordPress plugin',WPPH_PLUGIN_TEXT_DOMAIN)),
107
- 5002 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User deactivated a WordPress plugin',WPPH_PLUGIN_TEXT_DOMAIN)),
108
- 5003 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User uninstalled a plugin',WPPH_PLUGIN_TEXT_DOMAIN)),
109
- 5004 => array('type' => WPPH_E_WARNING_TEXT, 'text' => __('User upgraded a plugin',WPPH_PLUGIN_TEXT_DOMAIN)),
110
- ),
111
- 'Settings_And_System_Activity' => array(
112
- 6000 => array('type' => WPPH_E_NOTICE_TEXT,'text' => __('Security alerts automatically pruned by system',WPPH_PLUGIN_TEXT_DOMAIN)),
113
- 6001 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Option Anyone Can Register in WordPress settings changed',WPPH_PLUGIN_TEXT_DOMAIN)),
114
- 6002 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('New User Default Role changed',WPPH_PLUGIN_TEXT_DOMAIN)),
115
- 6003 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('WordPress Administrator Notification email changed',WPPH_PLUGIN_TEXT_DOMAIN))
116
- ),
117
- 'MultiSite' => array(
118
- 4008 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Granted Super Admin privileges to user',WPPH_PLUGIN_TEXT_DOMAIN)),
119
- 4009 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Revoked Super Admin privileges from user',WPPH_PLUGIN_TEXT_DOMAIN)),
120
- 4010 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Existent user added to site',WPPH_PLUGIN_TEXT_DOMAIN)),
121
- 4011 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('User removed from site',WPPH_PLUGIN_TEXT_DOMAIN)),
122
- 4012 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('New network user created',WPPH_PLUGIN_TEXT_DOMAIN)),
123
- 7000 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Site added to network',WPPH_PLUGIN_TEXT_DOMAIN)),
124
- 7001 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Site archived',WPPH_PLUGIN_TEXT_DOMAIN)),
125
- 7002 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Site unarchived',WPPH_PLUGIN_TEXT_DOMAIN)),
126
- 7003 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Site activated',WPPH_PLUGIN_TEXT_DOMAIN)),
127
- 7004 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Site deactivated',WPPH_PLUGIN_TEXT_DOMAIN)),
128
- 7005 => array('type' => WPPH_E_HIGH_TEXT, 'text' => __('Site deleted',WPPH_PLUGIN_TEXT_DOMAIN)),
129
- ),
130
- );
131
- }
132
-
133
-
134
- /**
135
- * @return bool
136
- * Convenient method to check whether or not the plugin's resources can be loaded
137
- */
138
- public static function canLoad() { return ((false === ($pos = stripos($_SERVER['REQUEST_URI'], WPPH_PLUGIN_PREFIX))) ? false : true); }
139
-
140
- public static function loadBaseResources()
141
- {
142
- if(self::canLoad())
143
- {
144
- wp_enqueue_style('wpph_styles_base', WPPH_PLUGIN_URL . 'res/css/styles.base.css');
145
- wp_enqueue_script('wpph-alvm-js', WPPH_PLUGIN_URL . 'res/js/audit-view-model.js', array('wpph-jcookie-js', 'wpph-ko-js'));
146
- wp_enqueue_script('wpph-ko-js', WPPH_PLUGIN_URL . 'res/js/knockout.js', array('jquery'));
147
- wp_enqueue_script('wpph-jcookie-js', WPPH_PLUGIN_URL . 'res/js/jquery-ck.js', array('jquery'));
148
- }
149
- }
150
-
151
- public static function createPluginWpSidebar()
152
- {
153
- $reqCap = self::$requiredCapMenu;
154
-
155
- if (!function_exists('add_menu_page'))
156
- {
157
- wpphLog('The required function "add_menu_page" to create the menu is not available on this installation.');
158
- return;
159
- }
160
-
161
- if(WPPHUtil::isAdministrator(wp_get_current_user()->ID)){
162
- self::_createMenu($reqCap, true, true, true);
163
- }
164
- elseif (WPPHUtil::isAllowedChange()){
165
- self::_createMenu($reqCap, true, true);
166
- }
167
- elseif(WPPHUtil::isAllowedAccess()){
168
- self::_createMenu($reqCap, true);
169
- }
170
- }
171
-
172
- private static function _createMenu($reqCap, $allowedAccess = false, $allowedChange = false, $isAdministrator = false)
173
- {
174
- if($isAdministrator || $allowedChange){
175
- add_menu_page('WP Security Audit Log', 'WP Security Audit Log', $reqCap, self::$baseMenuSlug, 'WPPH::pageMain', WPPH_PLUGIN_URL.'res/img/logo-main-menu.png');
176
- add_submenu_page(self::$baseMenuSlug, 'Audit Log Viewer', 'Audit Log Viewer', $reqCap, self::$baseMenuSlug, 'WPPH::pageMain');
177
- if(WPPHUtil::isMainSite()){
178
- add_submenu_page(self::$baseMenuSlug, __('Settings',WPPH_PLUGIN_TEXT_DOMAIN), __('Settings',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'settings', 'WPPH::pageSettings');
179
- add_submenu_page(self::$baseMenuSlug, __('Enable/Disable Alerts',WPPH_PLUGIN_TEXT_DOMAIN), __('Enable/Disable Alerts',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'alerts', 'WPPH::pageAlerts');
180
- }
181
- else {
182
- if(WPPHUtil::isAllowedChange()){
183
- add_submenu_page(self::$baseMenuSlug, __('Settings',WPPH_PLUGIN_TEXT_DOMAIN), __('Settings',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'settings', 'WPPH::pageSettings');
184
- add_submenu_page(self::$baseMenuSlug, __('Enable/Disable Alerts',WPPH_PLUGIN_TEXT_DOMAIN), __('Enable/Disable Alerts',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'alerts', 'WPPH::pageAlerts');
185
- }
186
- }
187
- add_submenu_page(self::$baseMenuSlug, __('About',WPPH_PLUGIN_TEXT_DOMAIN), __('About',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'about', 'WPPH::pageAbout');
188
- add_submenu_page(self::$baseMenuSlug, __('Support',WPPH_PLUGIN_TEXT_DOMAIN), __('Support',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'support', 'WPPH::pageSupport');
189
- }
190
- elseif($allowedAccess){
191
- add_menu_page('WP Security Audit Log', 'WP Security Audit Log', $reqCap, self::$baseMenuSlug, 'WPPH::pageMain', WPPH_PLUGIN_URL.'res/img/logo-main-menu.png');
192
- add_submenu_page(self::$baseMenuSlug, 'Audit Log Viewer', 'Audit Log Viewer', $reqCap, self::$baseMenuSlug, 'WPPH::pageMain');
193
- if(WPPHUtil::isAllowedChange()){
194
- add_submenu_page(self::$baseMenuSlug, __('Settings',WPPH_PLUGIN_TEXT_DOMAIN), __('Settings',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'settings', 'WPPH::pageSettings');
195
- add_submenu_page(self::$baseMenuSlug, __('Enable/Disable Alerts',WPPH_PLUGIN_TEXT_DOMAIN), __('Enable/Disable Alerts',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'alerts', 'WPPH::pageAlerts');
196
- }
197
- add_submenu_page(self::$baseMenuSlug, __('About',WPPH_PLUGIN_TEXT_DOMAIN), __('About',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'about', 'WPPH::pageAbout');
198
- add_submenu_page(self::$baseMenuSlug, __('Support',WPPH_PLUGIN_TEXT_DOMAIN), __('Support',WPPH_PLUGIN_TEXT_DOMAIN), $reqCap, self::$baseMenuSlug.'support', 'WPPH::pageSupport');
199
- }
200
- }
201
-
202
- public static function pageMain() { include(WPPH_PLUGIN_DIR.'pages/dashboard.php'); }
203
- public static function pageSettings() { include(WPPH_PLUGIN_DIR.'pages/settings.php'); }
204
- public static function pageAlerts() {
205
- wp_enqueue_style('jquery-smoothness-ui-css', 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.21/themes/smoothness/jquery-ui.css');
206
- wp_enqueue_script('jquery-ui-core');
207
- wp_enqueue_script('jquery-ui-tabs');
208
- include(WPPH_PLUGIN_DIR.'pages/alerts.php');
209
- }
210
- public static function pageAbout() { include(WPPH_PLUGIN_DIR.'pages/about.php'); }
211
- public static function pageSupport() { include(WPPH_PLUGIN_DIR.'pages/support.php'); }
212
-
213
- /**
214
- * @since v0.5
215
- * Create, save and retrieve the default list of events
216
- * @return array
217
- */
218
- public static function createDefaultEventsList()
219
- {
220
- $alerts = array();
221
- $events = self::getDefaultEvents();
222
- if(!empty($events)){
223
- foreach($events as $section => $values){
224
- $alerts[$section] = array();
225
- $_events = array_keys($values);
226
- if(! empty($_events)){
227
- foreach($_events as $k){
228
- $alerts[$section][$k] = 1;
229
- }
230
- }
231
- }
232
- }
233
- wpph_updatePluginEventsList($alerts);
234
- return $alerts;
235
- }
236
-
237
- /**
238
- * @since v0.5
239
- * Retrieve the list of all events from database
240
- * @return array
241
- */
242
- static function getEvents(){
243
- $events = wpph_getPluginEventsList();
244
- if(false === $events){
245
- $events = self::createDefaultEventsList();
246
- }
247
- return $events;
248
- }
249
-
250
- public static function createPluginDefaultSettings()
251
- {
252
- $settings = new stdClass();
253
- $settings->daysToKeep = 0;
254
- $settings->eventsToKeep = WPPH_KEEP_MAX_EVENTS; // default delete option
255
- $settings->showEventsViewList = 50; // how many items to show in the event viewer by default
256
- $settings->lastCleanup = time();
257
- $settings->cleanupRan = 0;
258
- $settings->showDW = 1; // whether or not to show the dashboard widget. @since v0.4
259
-
260
- update_option(WPPH_PLUGIN_SETTING_NAME, $settings);
261
- self::createDefaultEventsList();
262
- wpphLog('Settings added.');
263
- return $settings;
264
- }
265
- public static function getPluginSettings()
266
- {
267
- $settings = get_option(WPPH_PLUGIN_SETTING_NAME);
268
- if(false == $settings){
269
- $settings = self::createPluginDefaultSettings();
270
- }
271
- return $settings;
272
- }
273
-
274
- /**
275
- * @param object $settings If this param is null, $settingName & $settingValue(this cannot be null) must be set
276
- * @param string $settingName Optional. Required if $settings is null
277
- * @param string $settingValue Optional. Required if $settings is null
278
- * @param bool $overrideCleanupRan Whether or not to override the cleanupRan option. Defaults to false
279
- */
280
- public static function updatePluginSettings($settings = null, $settingName = null, $settingValue=null, $overrideCleanupRan = false)
281
- {
282
- if(! is_null($settings)){
283
- if($overrideCleanupRan){
284
- $settings->lastCleanup = 0;
285
- $settings->cleanupRan = 0;
286
- }
287
- update_option(WPPH_PLUGIN_SETTING_NAME, $settings);
288
- return;
289
- }
290
-
291
- // name and value must be set!
292
- if(is_null($settingName) || is_null($settingValue)){
293
- return;
294
- }
295
-
296
- $settings = self::getPluginSettings();
297
- $settings->$settingName = $settingValue;
298
- if($overrideCleanupRan){
299
- $settings->lastCleanup = 0;
300
- $settings->cleanupRan = 0;
301
- }
302
- update_option(WPPH_PLUGIN_SETTING_NAME, $settings);
303
- wpphLog('Settings saved.', $settings);
304
- }
305
-
306
- public static function onPluginActivate($blogId=1)
307
- {
308
- if($blogId > 1){
309
- return true;
310
- }
311
-
312
- wpphLog(__METHOD__.'() triggered.');
313
-
314
- $canContinue = true;
315
-
316
- // Check: MySQL, PHP - without these there's not much left for this plugin to do
317
- if(! self::checkMySQL()){
318
- self::__addPluginError(__("Plugin could not be properly installed. The MySQL version installed on this server is less than 5.",WPPH_PLUGIN_TEXT_DOMAIN));
319
- $canContinue = false;
320
- }
321
- if(! self::checkPHP()){
322
- self::__addPluginError(__("Plugin could not be properly installed. The PHP version installed on this server is less than 5.",WPPH_PLUGIN_TEXT_DOMAIN));
323
- $canContinue = false;
324
- }
325
- // no need for further checks, the plugin cannot run on this server...
326
- if(! $canContinue){
327
- $GLOBALS['WPPH_CAN_RUN'] = false;
328
- self::__addPluginError(__("Plugin could not be properly installed because the server does not meet our requirements: MySQL and PHP version >= 5.",WPPH_PLUGIN_TEXT_DOMAIN));
329
- return false;
330
- }
331
-
332
- // check to see whether or not an upgrade is necessary
333
- global $wpdb;
334
- $dbUpdated = get_option(WPPH_PLUGIN_DB_UPDATED);
335
- $tablesExist = false;
336
- $triggerInstallEvent = false; // whether or not the plugin is installed
337
- $pluginDbVersion = get_option(WPPH_PLUGIN_VERSION_OPTION_NAME);
338
-
339
- delete_option(WPPH_PLUGIN_ERROR_OPTION_NAME);
340
-
341
- // first install?
342
- if($pluginDbVersion === false){
343
- // Check tables
344
- if(WPPHDatabase::tableExists($wpdb, WPPHDatabase::getFullTableName('MAIN')) || WPPHDatabase::tableExists($wpdb, WPPHDatabase::getFullTableName('EVENTS'))){
345
- $tablesExist = true;
346
- }
347
- else { $triggerInstallEvent = true; }
348
- }
349
-
350
- // if we need to install
351
- if($triggerInstallEvent)
352
- {
353
- if($dbUpdated){ delete_option(WPPH_PLUGIN_DB_UPDATED); }
354
- if(WPPHDatabase::handleDatabase()){
355
- self::__handlePluginActivation(true);
356
- return true;
357
- }
358
- else {
359
- self::__addPluginError(__("Plugin could not be properly installed because we have encountered errors during the database update.",WPPH_PLUGIN_TEXT_DOMAIN));
360
- return false;
361
- }
362
- }
363
- // plugin already installed
364
- else
365
- {
366
- // if tables exist - do update database
367
- if($tablesExist)
368
- {
369
- // check plugin version
370
- if(empty($pluginDbVersion))
371
- {
372
- if($dbUpdated){ delete_option(WPPH_PLUGIN_DB_UPDATED); }
373
- // maybe version 0.1 ? empty tables
374
- if(! WPPHDatabase::v2Cleanup()){
375
- self::__addPluginError(__("Plugin could not be properly installed because we have encountered errors during the database update.",WPPH_PLUGIN_TEXT_DOMAIN));
376
- return false;
377
- }
378
- // update database
379
- if(WPPHDatabase::handleDatabase()){
380
- self::__handlePluginActivation();
381
- return true;
382
- }
383
- else {
384
- self::__addPluginError(__("Plugin could not be properly installed because we have encountered errors during the database update.",WPPH_PLUGIN_TEXT_DOMAIN));
385
- return false;
386
- }
387
- }
388
- else {
389
- $pluginDbVersion = (float)$pluginDbVersion;
390
- $currentVersion = (float)WPPH_PLUGIN_VERSION;
391
- // no need for upgrade
392
- if(version_compare($pluginDbVersion, $currentVersion, '==')){
393
- self::__handlePluginActivation();
394
- return true;
395
- }
396
- }
397
- }
398
- // tables not found
399
- else {
400
- if($dbUpdated){ delete_option(WPPH_PLUGIN_DB_UPDATED); }
401
- // create them
402
- if(WPPHDatabase::handleDatabase()){
403
- self::__handlePluginActivation();
404
- return true;
405
- }
406
- else {
407
- self::__addPluginError(__("Plugin could not be properly installed because we have encountered errors during the database update.",WPPH_PLUGIN_TEXT_DOMAIN));
408
- return false;
409
- }
410
- }
411
- }
412
- return false;
413
- }
414
-
415
- /**
416
- * Triggered when the plugin is deactivated
417
- * @param int $blogId
418
- * @return bool true
419
- */
420
- public static function onPluginDeactivate($blogId=1)
421
- {
422
- wpphLog(__FUNCTION__.'() triggered.');
423
- wp_clear_scheduled_hook(WPPH_PLUGIN_DEL_EVENTS_CRON_TASK_NAME);
424
- delete_option(WPPH_PLUGIN_ERROR_OPTION_NAME);
425
- update_option('WPPH_PLUGIN_ACTIVATED',0);
426
- return true;
427
- }
428
-
429
- public static function __addPluginError($error)
430
- {
431
- $data = get_option(WPPH_PLUGIN_ERROR_OPTION_NAME);
432
- if(empty($data)){
433
- $data = array();
434
- }
435
- $data[] = base64_encode($error);
436
- update_option(WPPH_PLUGIN_ERROR_OPTION_NAME, $data);
437
- return true;
438
- }
439
-
440
- private static function __handlePluginActivation($triggerInstallEvent = false)
441
- {
442
- self::getPluginSettings();
443
-
444
- $GLOBALS['WPPH_CAN_RUN'] = true;
445
- update_option(WPPH_PLUGIN_DB_UPDATED,1);
446
- delete_option(WPPH_PLUGIN_ERROR_OPTION_NAME);
447
- update_option(WPPH_PLUGIN_VERSION_OPTION_NAME, WPPH_PLUGIN_VERSION);
448
- WPPHUtil::saveInitialAccessChangeList();
449
- if($triggerInstallEvent)
450
- {
451
- if(! defined('WPPH_PLUGIN_INSTALLED_OK')) {
452
- //@see: WPPHEventWatcher::watchPluginInstall()
453
- define('WPPH_PLUGIN_INSTALLED_OK',true);
454
- }
455
- // log plugin installation
456
- wpph_installPlugin(WPPH_PLUGIN_NAME, wp_get_current_user()->ID, WPPHUtil::getIP());
457
- }
458
- // log plugin activation
459
- WPPHEvent::hookWatchPluginActivity();
460
-
461
- // register cron job for events deletion
462
- if(defined('DISABLE_WP_CRON') && DISABLE_WP_CRON){ return true; }
463
- else
464
- {
465
- if ( ! wp_next_scheduled(WPPH_PLUGIN_DEL_EVENTS_CRON_TASK_NAME)) {
466
- $interval = (defined('WPPH_CLEANUP_INTERVAL') ? WPPH_CLEANUP_INTERVAL : 'hourly');
467
- wp_schedule_event( time(), $interval, WPPH_PLUGIN_DEL_EVENTS_CRON_TASK_NAME );
468
- wpphLog(__METHOD__.'() '.WPPH_PLUGIN_DEL_EVENTS_CRON_TASK_NAME.' task scheduled by wp-cron. Time Interval set to: '.$interval);
469
- }
470
- }
471
- }
472
- /**
473
- * @internal
474
- * @static
475
- * @var array Holds the list or errors generated during install
476
- */
477
- private static $_errors = array();
478
-
479
- // must only be called in pages
480
- public static function ready()
481
- {
482
- if(empty(self::$_errors)){
483
- self::$_errors = self::getPluginErrors();
484
- if(empty(self::$_errors)){
485
- return true;
486
- }
487
- }
488
- return false;
489
- }
490
-
491
- public static function getPluginErrors() { return get_option(WPPH_PLUGIN_ERROR_OPTION_NAME); }
492
-
493
- public static function checkMySQL(){
494
- global $wpdb;
495
- $v = $wpdb->get_var("SELECT VERSION();");
496
- if(empty($v)){ return false; }
497
- $v = trim($v);
498
- if(intval($v[0]) < 5){ return false; }
499
- return true;
500
- }
501
- public static function checkPHP(){ return (version_compare(phpversion(), '5.0.0', '>=')); }
502
-
503
-
504
- /**
505
- * Check to see whether or not this is a multisite instance
506
- * @since v0.6
507
- * @return bool
508
- */
509
- static function isMultisite(){ return ((function_exists('is_multisite') && is_multisite()) ? true : false); }
510
-
511
- }
512
-
513
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/WPPHDatabase.php DELETED
@@ -1,385 +0,0 @@
1
- <?php
2
- /**
3
- * Class WPPHDatabase
4
- * Internal class handling the creation, update and upgrade of the db tables
5
- */
6
- class WPPHDatabase
7
- {
8
- /**
9
- * @var bool
10
- * Whether or not we can safely use the plugin
11
- */
12
- private static $_canRun = false;
13
-
14
- private static $_canUpgrade = false;
15
-
16
- /**
17
- * @var string
18
- * @private
19
- * Holds the name of the event logs table WITHOUT the db prefix. As of version 0.6 it has been changed to: wordpress_auditlog
20
- */
21
- private static $_eventsLogTableBaseName = 'wordpress_auditlog'; //old: {prefix}_wordpress_eventlog
22
-
23
- /**
24
- * @var string
25
- * @private
26
- * Holds the name of the events details table WITHOUT the db prefix. As of version 0.6 it has been changed to: wordpress_auditlog_events
27
- */
28
- private static $_eventsDetailsTableBaseName = 'wordpress_auditlog_events'; //old: {prefix}_wordpress_eventlog_details
29
-
30
-
31
- private static $_tablesCreated = false;
32
- private static $_tablesUpgraded = false;
33
- private static $_tablesUpdated = false;
34
-
35
-
36
- //================================================================================================================
37
-
38
- // since v0.6
39
- static function dropTables()
40
- {
41
- $version = WPPHNetwork::getGlobalOption(WPPH_PLUGIN_VERSION_OPTION_NAME, false, true, null);
42
- if(is_null($version)){
43
- return;
44
- }
45
- if(version_compare($version,'0.6','>=')){
46
- return;
47
- }
48
-
49
- global $wpdb;
50
- if(self::tableExists($wpdb, self::getFullTableName('MAIN')) && self::tableExists($wpdb, self::getFullTableName('EVENTS'))){
51
- return;
52
- }
53
-
54
- WPPHNetwork::updateGlobalOption(WPPH_PLUGIN_DB_UPDATED, 0, false, true);
55
- $prefix = (WPPH::isMultisite() ? self::getDefaultPrefix() : $wpdb->prefix);
56
-
57
- $tMainOld = $prefix.'_wordpress_eventlog';
58
- $tEventsOld = $prefix.'_wordpress_eventlog_details';
59
-
60
- if(self::tableExists($wpdb, $tMainOld)){
61
- $q = "DROP TABLE IF EXISTS {$tMainOld}";
62
- $r = $wpdb->query($q);
63
- wpphLog("Old table {$tMainOld} found. Deleting table.", array('query'=>$q, 'result'=> $r===true?'success':'failed'));
64
- }
65
- if(self::tableExists($wpdb, $tEventsOld)){
66
- $q = "DROP TABLE IF EXISTS {$tEventsOld}";
67
- $r = $wpdb->query($q);
68
- wpphLog("Old table {$tEventsOld} found. Deleting table.", array('query'=>$q, 'result'=> $r===true?'success':'failed'));
69
- }
70
- }
71
-
72
-
73
- static function handleDatabase()
74
- {
75
- // Check database
76
- $dbUpdated = get_option(WPPH_PLUGIN_DB_UPDATED);
77
- if(false !== $dbUpdated){
78
- self::$_tablesCreated = true;
79
- self::$_tablesUpgraded = true;
80
- self::$_tablesUpdated = true;
81
- self::$_canRun = true;
82
- return true;
83
- }
84
-
85
- global $wpdb;
86
- $tableMain = self::getFullTableName('MAIN');
87
- $tableEvents = self::getFullTableName('EVENTS');
88
-
89
- // Check if tables exist
90
- if(! self::tableExists($wpdb, $tableMain)){
91
- if(! self::_createEventLogsTable($wpdb, $tableMain)){
92
- WPPH::__addPluginError(__("Plugin cannot create tables in the WordPress database to store security audit logs. Allow write access to the WordPress database user temporarily to activate this plugin.
93
- For more information contact us on support@wpprohelp.com.",WPPH_PLUGIN_TEXT_DOMAIN));
94
- return false;
95
- }
96
- }
97
- if(! self::tableExists($wpdb, $tableEvents)){
98
- if(! self::_createEventDetailsTable($wpdb, $tableEvents)){
99
- WPPH::__addPluginError(__("Plugin cannot create tables in the WordPress database to store security audit logs. Allow write access to the WordPress database user temporarily to activate this plugin.
100
- For more information contact us on support@wpprohelp.com.",WPPH_PLUGIN_TEXT_DOMAIN));
101
- return false;
102
- }
103
- }
104
- // Check if tables need to be upgraded
105
- if(! self::_upgradeEventLogsTable($wpdb, $tableMain)){
106
- return false;
107
- }
108
- if(! self::_upgradeEventDetailsTable($wpdb, $tableEvents)){
109
- return false;
110
- }
111
- // Check if tables need to be updated
112
- if(! self::_updateEventsDetailsTable($wpdb, $tableEvents)){
113
- WPPH::__addPluginError(sprintf(__("Error updating table <strong>%s</strong>.",WPPH_PLUGIN_TEXT_DOMAIN), $tableEvents));
114
- return false;
115
- }
116
- if(! self::_updateEventLogsTable($wpdb, $tableMain)){
117
- WPPH::__addPluginError(sprintf(__("Error updating table <strong>%s</strong>.",WPPH_PLUGIN_TEXT_DOMAIN), $tableMain));
118
- return false;
119
- }
120
- self::$_tablesCreated = true;
121
- self::$_tablesUpgraded = true;
122
- self::$_tablesUpdated = true;
123
- self::$_canRun = true;
124
- return true;
125
- }
126
-
127
- static function tableExists($wpdb, $tableFullName)
128
- {
129
- $result = $wpdb->get_var("SHOW TABLES LIKE '$tableFullName'");
130
- return (is_null($result) ? false : true);
131
- }
132
-
133
- /**
134
- * Returns the full table name db_prefix + base_table_name for the requested table
135
- * @param string $what the table identifier. Possible values:
136
- * main -> to retrieve : db_prefix + self::$_eventsLogTableBaseName
137
- * events -> to retrieve: db_prefix + self::$_eventsDetailsTableBaseName
138
- * @return string
139
- */
140
- static function getFullTableName($what = 'main')
141
- {
142
- global $wpdb;
143
- if(strcasecmp($what, 'MAIN') == 0){
144
- return (WPPH::isMultisite() ? self::getDefaultPrefix() : $wpdb->prefix) . self::$_eventsLogTableBaseName;
145
- }
146
- elseif(strcasecmp($what, 'EVENTS') == 0){
147
- return (WPPH::isMultisite() ? self::getDefaultPrefix() : $wpdb->prefix) . self::$_eventsDetailsTableBaseName;
148
- }
149
- return '';
150
- }
151
-
152
- static function canRun() { return self::$_canRun; }
153
-
154
- /**
155
- * @internal
156
- * Prepares the tables for future upgrades from v0.1
157
- */
158
- static function v2Cleanup()
159
- {
160
- global $wpdb;
161
-
162
- $t1 = self::getFullTableName('MAIN');
163
- $t2 = self::getFullTableName('EVENTS');
164
-
165
- // empty table 1
166
- $query = "TRUNCATE ". $wpdb->prefix.self::$_eventsLogTableBaseName;
167
- if(false === $wpdb->query($query)){
168
- WPPH::__addPluginError(
169
- sprintf(
170
- __("Plugin could not be properly upgraded because we could not empty the content of the following table: <strong>%s</strong>",WPPH_PLUGIN_TEXT_DOMAIN),$t1)
171
- );
172
- self::$_canUpgrade = false;
173
- }
174
- else { self::$_canUpgrade = true; }
175
-
176
- // empty table 2
177
- $query = "TRUNCATE ".$wpdb->prefix.self::$_eventsDetailsTableBaseName;
178
- if(false === $wpdb->query($query)){
179
- WPPH::__addPluginError(
180
- sprintf(__("Plugin could not be properly upgraded because we could not empty the content of the following table: <strong>%s</strong>",WPPH_PLUGIN_TEXT_DOMAIN),$t2)
181
- );
182
- self::$_canUpgrade = false;
183
- }
184
- else { self::$_canUpgrade = true; }
185
-
186
- return self::$_canUpgrade;
187
- }
188
-
189
- private static function _createEventLogsTable($wpdb, $tableFullName)
190
- {
191
- $query = "CREATE TABLE IF NOT EXISTS `$tableFullName` (
192
- `EventNumber` bigint(40) NOT NULL AUTO_INCREMENT,
193
- `EventID` int(8) NOT NULL,
194
- `EventDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
195
- `UserID` int(8) NOT NULL DEFAULT '0',
196
- `UserIP` varchar(24) NOT NULL DEFAULT '0.0.0.0',
197
- `EventData` TEXT NOT NULL,
198
- PRIMARY KEY (`EventNumber`),
199
- UNIQUE KEY `EventNumber` (`EventNumber`)
200
- );";
201
- if(false === @$wpdb->query($query)){return false;}
202
- return true;
203
- }
204
- private static function _createEventDetailsTable($wpdb, $tableFullName)
205
- {
206
- $query = "CREATE TABLE IF NOT EXISTS `$tableFullName` (
207
- `EventID` int(8) NOT NULL,
208
- `EventType` varchar(10) DEFAULT 'NOTICE',
209
- `EventDescription` text NOT NULL,
210
- PRIMARY KEY (`EventID`),
211
- UNIQUE KEY `EventID` (`EventID`)
212
- );";
213
- if (false === @$wpdb->query($query)){ return false; }
214
- return true;
215
- }
216
- private static function _upgradeEventLogsTable($wpdb, $tableFullName)
217
- {
218
- $q = "SHOW COLUMNS FROM $tableFullName LIKE 'EventCount';";
219
- $rowData = $wpdb->get_row($q, ARRAY_A);
220
- if(empty($rowData['Field']))
221
- {
222
- $q = "ALTER TABLE $tableFullName ADD COLUMN `EventCount` INT NOT NULL DEFAULT 1 AFTER `EventData`;";
223
- $result = @$wpdb->query($q);
224
- if($result === false){
225
- WPPH::__addPluginError(
226
- sprintf(__("Plugin could not be properly installed. The db user used to connect to the WordPress database is missing the <strong>ALTER</strong> right for query: <strong>%s</strong>",WPPH_PLUGIN_TEXT_DOMAIN),$q)
227
- );
228
- return false;
229
- }
230
- $q = "ALTER TABLE $tableFullName ADD COLUMN `UserName` VARCHAR(125) NOT NULL DEFAULT '' AFTER `EventCount`;";
231
- $result = @$wpdb->query($q);
232
- if($result === false){
233
- WPPH::__addPluginError(
234
- sprintf(__("Plugin could not be properly installed. The db user used to connect to the WordPress database is missing the <strong>ALTER</strong> right for query: <strong>%s</strong>",WPPH_PLUGIN_TEXT_DOMAIN),$q)
235
- );
236
- return false;
237
- }
238
- }
239
- $q = "SHOW COLUMNS FROM $tableFullName LIKE 'BlogId';";
240
- $rowData = $wpdb->get_row($q, ARRAY_A);
241
- if(empty($rowData['Field']))
242
- {
243
- $q = "ALTER TABLE $tableFullName ADD COLUMN `BlogId` INT NOT NULL DEFAULT 1 AFTER `UserName`;";
244
- $result = @$wpdb->query($q);
245
- if($result === false){
246
- WPPH::__addPluginError(
247
- sprintf(__("Plugin could not be properly installed. The db user used to connect to the WordPress database is missing the <strong>ALTER</strong> right for query: <strong>%s</strong>",WPPH_PLUGIN_TEXT_DOMAIN),$q)
248
- );
249
- return false;
250
- }
251
- }
252
- return true;
253
- }
254
- private static function _upgradeEventDetailsTable($wpdb, $tableFullName)
255
- {
256
- return true;
257
- }
258
- /**
259
- * This function will insert the default rows in the events details table
260
- */
261
- private static function _updateEventsDetailsTable($wpdb, $tableFullName)
262
- {
263
- $queries = array();
264
- $events = WPPHEvent::listEvents();
265
- if(empty($events)){ return true; }
266
-
267
- // check for differences
268
- $numFileEvents = count($events);
269
- $numDbEvents = (int)$wpdb->get_var("SELECT COUNT(EventID) FROM $tableFullName;");
270
-
271
- // no update necessary
272
- if($numFileEvents == $numDbEvents){
273
- return true;
274
- }
275
-
276
- foreach($events as $entry)
277
- {
278
- $q = sprintf("INSERT INTO $tableFullName (`EventID`,`EventType`,`EventDescription`) VALUES(%d,'%s','%s')", $entry['id'], $entry['category'], $entry['text']);
279
- $queries["{$entry['id']}"] = $q;
280
- }
281
-
282
- // Clear table
283
- if($numDbEvents > 0){
284
- $result = @$wpdb->query("TRUNCATE $tableFullName");
285
- if($result === false){
286
- WPPH::__addPluginError(
287
- sprintf(
288
- __("Could not empty table <strong>%s</strong>. Please run the following query manually: <strong>TRUNCATE %s</strong>",WPPH_PLUGIN_TEXT_DOMAIN)
289
- ,$tableFullName, $tableFullName)
290
- );
291
- return false;
292
- }
293
- }
294
- // Insert data
295
- foreach($queries as $id => $query){
296
- if(! empty($query)){
297
- if(false === @$wpdb->query($query)){
298
- wpphLog('QUERY FAILED TO RUN: ',$query);
299
- WPPH::__addPluginError(
300
- sprintf(
301
- __("Error updating table <strong>%s</strong> using query: <strong>%s</strong>",WPPH_PLUGIN_TEXT_DOMAIN)
302
- ,$tableFullName, $query)
303
- );
304
- return false;
305
- }
306
- }
307
- }
308
- return true;
309
- }
310
-
311
- private static function _updateEventLogsTable($wpdb, $tableFullName)
312
- {
313
- return true;
314
- }
315
-
316
- /**
317
- * Retrieve the default database prefix
318
- * @since v0.6
319
- * @uses global var $wpdb
320
- * @return string
321
- */
322
- static function getDefaultPrefix(){
323
- global $wpdb;
324
- return $wpdb->base_prefix;
325
- }
326
- }
327
-
328
- /**
329
- * Class WPPHDB
330
- * Contains utility methods to communicate with the database
331
- */
332
- class WPPHDB extends WPPHDatabase
333
- {
334
- /**
335
- * @return string The current logged in user's role
336
- */
337
- static function getCurrentUserRole()
338
- {
339
- global $current_user;
340
- get_currentuserinfo();
341
- $user_roles = $current_user->roles;
342
- $user_role = array_shift($user_roles);
343
- return $user_role;
344
- }
345
- // returns array(userName, userRole)
346
- static function getUserInfo($userID)
347
- {
348
- if(empty($userID)){
349
- wpphLog(__METHOD__.'() called with an invalid argument $userID. Ignoring request.', array('$userID'=>$userID));
350
- return array('userName'=>'', 'userRole'=>'');
351
- }
352
- wpphLog(__METHOD__.'() called.', array('$userID'=>$userID));
353
-
354
- global $wpdb;
355
-
356
- $t = $wpdb->users;
357
-
358
- $username = $wpdb->get_var("SELECT user_login FROM $t WHERE ID=$userID");
359
- $user = new WP_User( $userID );
360
- $userRole = (empty($user->roles[0]) ? '' : $user->roles[0]);
361
-
362
- wpphLog("Function: ".__METHOD__." User info", array('id'=>$userID, 'roles'=>$user->roles));
363
-
364
- return array(
365
- 'userName' => $username,
366
- 'userRole' => $userRole
367
- );
368
- }
369
-
370
- /**
371
- * Retrieve the total number of events from db
372
- * @param integer $blogId
373
- * @return int
374
- */
375
- static function getEventsCount($blogId = 1)
376
- {
377
- global $wpdb;
378
- $where = '';
379
- if(!empty($blogId)){
380
- $where = " WHERE BlogId = ".intval($blogId);
381
- return $wpdb->get_var("SELECT COUNT(EventNumber) FROM ".self::getFullTableName('main').$where);
382
- }
383
- return $wpdb->get_var("SELECT COUNT(EventNumber) FROM ".self::getFullTableName('main'));
384
- }
385
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/WPPHEvent.php DELETED
@@ -1,2213 +0,0 @@
1
- <?php
2
- /**
3
- * WPPHEvent
4
- */
5
- class WPPHEvent
6
- {
7
- /**
8
- * Retrieve the list of events
9
- * @return array
10
- */
11
- static function listEvents()
12
- {
13
- return array(
14
- // 1xxx - Login/Logout events
15
- array( 'id' => 1000, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Successfully logged in.',WPPH_PLUGIN_TEXT_DOMAIN)),
16
- array( 'id' => 1001, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Successfully logged out.',WPPH_PLUGIN_TEXT_DOMAIN)),
17
- array( 'id' => 1002, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Failed login detected using <strong>%s</strong> as username.',WPPH_PLUGIN_TEXT_DOMAIN)),
18
-
19
- // 2xxx - User activity events
20
- // Created a new blog post called %Post Title%. Blog post ID is %ID%
21
- array( 'id' => 2000, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Created a new draft blog post called <strong>%s</strong>. Blog post ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
22
- // Published a blog post called %Post_Title%. Blog post URL is %Post_URL%
23
- array( 'id' => 2001, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Published a blog post called <strong>%s</strong>. Blog post URL is <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
24
- // Modified the published blog post %post_title%. Blog post URL is %post_URL%
25
- array( 'id' => 2002, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Modified the published blog post <strong>%s</strong>. Blog post URL is <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
26
- // Modified the draft blog post %post_title%. Blog post ID is %ID%
27
- array( 'id' => 2003, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Modified the draft blog post <strong>%s</strong>. Blog post ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
28
-
29
- // Created a new page called %page_title%. Page ID is %ID%
30
- array( 'id' => 2004, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Created a new draft page called <strong>%s</strong>. Page ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
31
- // Published a page called %page_title%. Page URL is %URL%
32
- array( 'id' => 2005, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Published a page called <strong>%s</strong>. Page URL is <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
33
- // Modified the published page %page_title%. Page URL is %URL%
34
- array( 'id' => 2006, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Modified the published page <strong>%s</strong>. Page URL is <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
35
- // Modified the draft page %page_title%. Page ID is %ID%
36
- array( 'id' => 2007, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Modified the draft page <strong>%s</strong>. Page ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
37
- // Deleted the post %Title%. Blog post ID is %ID%
38
- array( 'id' => 2008, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Deleted the post <strong>%s</strong>. Blog post ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
39
- // Deleted the page %Title%. Page ID is %ID%
40
- array( 'id' => 2009, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Deleted the page <strong>%s</strong>. Page ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
41
-
42
- // Uploaded the file %file name$ in %file location%
43
- array( 'id' => 2010, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Uploaded the file <strong>%s</strong> in <strong>%s</strong>/.',WPPH_PLUGIN_TEXT_DOMAIN)),
44
- // Deleted file %file name$ from %file_location%
45
- array( 'id' => 2011, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Deleted the file <strong>%s</strong> from <strong>%s</strong>/.',WPPH_PLUGIN_TEXT_DOMAIN)),
46
- // 2012 - trashed draft post
47
- array( 'id' => 2012, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Moved the post <strong>%s</strong> to trash.',WPPH_PLUGIN_TEXT_DOMAIN)),
48
- // 2013 - trashed published post
49
- array( 'id' => 2013, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Moved the page <strong>%s</strong> to trash.',WPPH_PLUGIN_TEXT_DOMAIN)),
50
- // 2014 - untrashed post
51
- array( 'id' => 2014, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Post <strong>%s</strong> has been restored from trash.',WPPH_PLUGIN_TEXT_DOMAIN)),
52
- // 2015 - untrashed page
53
- array( 'id' => 2015, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Page <strong>%s</strong> has been restored from trash.',WPPH_PLUGIN_TEXT_DOMAIN)),
54
- // 2016 - Post category changed
55
- array( 'id' => 2016, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the category(ies) of the post <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
56
- // 2017 - Changed the URL of the post %post_name% from %old_url% to %new_url%
57
- array( 'id' => 2017, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the URL of the post <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
58
- // 2018 - Changed the URL of the page %page_name% from %old_url% to %new_url%
59
- array( 'id' => 2018, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the URL of the page <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
60
- // 2019 - Changed the author of %post_name% post from %old_author% to %new_author%
61
- array( 'id' => 2019, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the author of <strong>%s</strong> post from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
62
- // 2020 - Changed the author of %page_name% page from %old_author% to %new_author%
63
- array( 'id' => 2020, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the author of <strong>%s</strong> page from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
64
- // 2021 - %postName% from %oldStatus% to %newStatus%
65
- array( 'id' => 2021, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the status of <strong>%s</strong> post from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
66
- // 2022 - page from published to draft
67
- array( 'id' => 2022, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the status of <strong>%s</strong> page from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
68
- // 2023 - added new category
69
- array( 'id' => 2023, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Created a new category called <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
70
- // 2024 - deleted category
71
- array( 'id' => 2024, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Deleted the <strong>%s</strong> category.',WPPH_PLUGIN_TEXT_DOMAIN)),
72
- // 2025 - Changed the visibility of %post_name% blog post from %old_visibility% to %new_visibility%
73
- array( 'id' => 2025, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Changed the visibility of <strong>%s</strong> blog post from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
74
- // 2026 - Changed the visibility of %page_name% page from %old_visibility% to %new_visibility%
75
- array( 'id' => 2026, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Changed the visibility of <strong>%s</strong> page from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
76
- // 2027 - Changed the date of %post_name% blog post from %old_date% to %new_date%
77
- array( 'id' => 2027, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the date of <strong>%s</strong> blog post from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
78
- // 2028 - Changed the date of %post_name% page from %old_date% to %new_date%
79
- array( 'id' => 2028, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the date of <strong>%s</strong> page from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
80
-
81
- //[[ Custom Post Types
82
- // 2029 Created a new custom post called %Post Title%. Post ID is %ID%
83
- array( 'id' => 2029, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Created a new draft custom post <strong>%s</strong> of type <strong>%s</strong>. Post ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
84
- // 2030 Published a custom post called %Post_Title%. Post URL is %Post_URL%
85
- array( 'id' => 2030, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Published a custom post <strong>%s</strong> of type <strong>%s</strong>. Post URL is <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
86
- // 2031 Modified the published custom post %post_title%. Post URL is %post_URL%
87
- array( 'id' => 2031, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Modified the custom post <strong>%s</strong> of type <strong>%s</strong>. Post URL is <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
88
- // 2032 Modified the draft custom post %post_title%. Post ID is %ID%
89
- array( 'id' => 2032, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Modified the draft custom post <strong>%s</strong> of type <strong>%s</strong>. Post ID is <strong>%d</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
90
- // 2033 Deleted the custom post %Title%. Post ID is %ID%
91
- array( 'id' => 2033, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Deleted custom post <strong>%s</strong> of type <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
92
- // 2034 - trashed draft custom post
93
- array( 'id' => 2034, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Moved the custom post <strong>%s</strong> to trash. Post type is <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
94
- // 2035 - untrashed custom post
95
- array( 'id' => 2035, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Custom post <strong>%s</strong> of type <strong>%s</strong> has been restored from trash.',WPPH_PLUGIN_TEXT_DOMAIN)),
96
- // 2036 - Custom post category changed
97
- array( 'id' => 2036, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the category(ies) of custom post <strong>%s</strong> of type <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
98
- // 2037 - Changed the URL of the custom post %post_name% from %old_url% to %new_url%
99
- array( 'id' => 2037, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the URL of the custom post <strong>%s</strong> of type <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
100
- // 2038 - Changed the author of %post_name% custom post from %old_author% to %new_author%
101
- array( 'id' => 2038, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the author of custom post <strong>%s</strong> of type <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
102
- // 2039 - Changed the status of %postName% custom post from %oldStatus% to %newStatus%
103
- array( 'id' => 2039, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the status of custom post <strong>%s</strong> of type <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
104
- // 2040 - Changed the visibility of %post_name% custom post from %old_visibility% to %new_visibility%
105
- array( 'id' => 2040, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Changed the visibility of custom post <strong>%s</strong> of type <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
106
- // 2041 - Changed the date of %post_name% custom post from %old_date% to %new_date%
107
- array( 'id' => 2041, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the date of custom post <strong>%s</strong> of type <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
108
-
109
- // WIDGETS
110
- // 2042 - Added a new %type% widget in %section%
111
- array( 'id' => 2042, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Added a new <strong>%s</strong> widget in <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
112
- // 2043 - Modified the %type% widget in %section%
113
- array( 'id' => 2043, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Modified the <strong>%s</strong> widget in <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
114
- // 2044 - Deleted the %type% widget from %section%
115
- array( 'id' => 2044, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Deleted the <strong>%s</strong> widget from <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
116
- // 2045 - Moved the %type% widget from %old_location% to %new_location%
117
- array( 'id' => 2045, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Moved the <strong>%s</strong> widget from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
118
-
119
- // 3xxx - Themes management
120
- // Activated the theme %themeName%
121
- array( 'id' => 3000, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Activated the theme <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
122
-
123
- // 4xxx - User profile events
124
- array( 'id' => 4000, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('A new user with the username <strong>%s</strong> has registered with the role of <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
125
- array( 'id' => 4001, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('<strong>%s</strong> created a new user <strong>%s</strong> with the role of <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
126
- array( 'id' => 4002, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('The role of user <strong>%s</strong> was changed from <strong>%s</strong> to <strong>%s</strong> by <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
127
- array( 'id' => 4003, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Changed the account password.',WPPH_PLUGIN_TEXT_DOMAIN)),
128
- array( 'id' => 4004, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('<strong>%s</strong> changed the password for user <strong>%s</strong> with the role of <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
129
- // Changed the email address from %old_email% to %new_email%
130
- array( 'id' => 4005, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Changed the email address from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
131
- // %user_making_change% changed the email address of user %user% from %old_email% to %new_email%
132
- array( 'id' => 4006, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('<strong>%s</strong> changed the email address of user <strong>%s</strong> from <strong>%s</strong> to <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
133
- // User %user% with the role of %role% was deleted by %user_deleting%
134
- array( 'id' => 4007, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('User <strong>%s</strong> with the role of <strong>%s</strong> was deleted by <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
135
-
136
- // 5xxx - Plugin management
137
- // # 5000 Installed the plugin %name%.
138
- array( 'id' => 5000, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Installed the plugin <strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
139
- // Activated the plugin %plugin_name% installed in %plugin_directory%
140
- array( 'id' => 5001, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Activated the plugin <strong>%s</strong> installed in /<strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
141
- // Deactivated the plugin %plugin_name% installed in %plugin_directory%
142
- array( 'id' => 5002, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Deactivated the plugin <strong>%s</strong> installed in /<strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
143
- // # 5003 Uninstalled the plugin %plugin_name% which was installed in %path%
144
- array( 'id' => 5003, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Uninstalled the plugin <strong>%s</strong> which was installed in /<strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
145
- // # 5004 Upgraded the plugin %name% installed in %path%
146
- array( 'id' => 5004, 'category' => WPPH_E_WARNING_TEXT, 'text' => __('Upgraded the plugin <strong>%s</strong> installed in /<strong>%s</strong>.',WPPH_PLUGIN_TEXT_DOMAIN)),
147
-
148
- // 6xxx - System events
149
- // #6000 Events automatically deleted by system.
150
- array( 'id' => 6000, 'category' => WPPH_E_NOTICE_TEXT, 'text' => __('Alerts automatically deleted by system.',WPPH_PLUGIN_TEXT_DOMAIN)),
151
- // #6001 - <strong>%s</strong> the option Anyone can register
152
- array( 'id' => 6001, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('<strong>%s</strong> the option Anyone can register',WPPH_PLUGIN_TEXT_DOMAIN)),
153
- // #6002 - Changed the New User Default Role from <strong>%s</strong> to <strong>%s</strong>
154
- array( 'id' => 6002, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Changed the New User Default Role from <strong>%s</strong> to <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
155
- // #6003 - Changed the WordPress administrator notifications email address from %old_email% to %new_mail%
156
- array( 'id' => 6003, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Changed the WordPress administrator notifications email address from <strong>%s</strong> to <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
157
-
158
-
159
- // xxxx - MultiSite Events
160
-
161
- // #4008 - Granted Super Admin privileges from <strong>%user%</strong>
162
- array( 'id' => 4008, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Granted Super Admin privileges to <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
163
- // #4009 - Revoked Super Admin privileges from <strong>%user%</strong>
164
- array( 'id' => 4009, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Revoked Super Admin privileges from <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
165
- // #4010 - Added existing user %user% with %role% role to site %site%
166
- array( 'id' => 4010, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Added existing user <strong>%s</strong> with role <strong>%s</strong> to site <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
167
- // #4011 - Removed user %user% with role %role% from %site% site
168
- array( 'id' => 4011, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Removed user <strong>%s</strong> with role <strong>%s</strong> from site <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
169
- // #4012 - Created a new network user %user%
170
- array( 'id' => 4012, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Created a new network user <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
171
- // #7000 - Added <strong>%site%</strong> to the network
172
- array( 'id' => 7000, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Added site <strong>%s</strong> to the network',WPPH_PLUGIN_TEXT_DOMAIN)),
173
- // #7001 - Archived site <strong>%site%</strong>
174
- array( 'id' => 7001, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Archived site <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
175
- // #7002 - Unarchived site <strong>%site%</strong>
176
- array( 'id' => 7002, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Unarchived site <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
177
- // #7003 - Activated site <strong>%site%</strong>
178
- array( 'id' => 7003, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Activated site <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
179
- // #7004 - Deactivated site <strong>%site%</strong>
180
- array( 'id' => 7004, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Deactivated site <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
181
- // #7005 - Deleted site <strong>%site%</strong>
182
- array( 'id' => 7005, 'category' => WPPH_E_HIGH_TEXT, 'text' => __('Deleted site <strong>%s</strong>',WPPH_PLUGIN_TEXT_DOMAIN)),
183
- );
184
- }
185
-
186
-
187
- // 1xxx - Login/Logout events
188
-
189
- // 1000
190
- static function hookLoginEvent() { add_action('wp_login', array('WPPHEventWatcher', 'watchEventLogin'), 10, 2); }
191
- // 1001
192
- static function hookLogoutEvent() { add_action('wp_logout', array('WPPHEventWatcher', 'watchEventLogout')); }
193
- // 1002
194
- static function hookLoginFailure() { add_action('wp_login_failed', array('WPPHEventWatcher', 'watchLoginFailure')); }
195
-
196
-
197
- // 2xxx - User activity events
198
-
199
- // 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2021, 2022
200
- static function hookWatchBlogActivity() { add_action('transition_post_status', array('WPPHEventWatcher', 'watchBlogActivity'), 10, 3); }
201
- // 2008, 2009
202
- static function hookFileDeletion() { add_action('delete_post', array('WPPHEventWatcher', 'watchTrash'), 10, 1); }
203
- // 2010
204
- static function hookFileUploaded() { add_action('add_attachment', array('WPPHEventWatcher', 'watchFileUploaded')); }
205
- // 2011
206
- static function hookFileUploadedDeleted() { add_action('delete_attachment', array('WPPHEventWatcher', 'watchFileUploadedDeleted')); }
207
- // 2012
208
- static function hookTrashPost() {
209
- if(defined('EMPTY_TRASH_DAYS') && (EMPTY_TRASH_DAYS == 0)){
210
- add_action('delete_post', array('WPPHEventWatcher', 'watchTrash'), 10, 1);
211
- }
212
- else { add_action('wp_trash_post', array('WPPHEventWatcher', 'watchFileDeletion')); }
213
- }
214
- // 2013
215
- static function hookTrashPage() {
216
- if(defined('EMPTY_TRASH_DAYS') && (EMPTY_TRASH_DAYS == 0)){
217
- add_action('delete_post', array('WPPHEventWatcher', 'watchTrash'), 10, 1);
218
- }
219
- else { add_action('wp_trash_page', array('WPPHEventWatcher', 'watchFileDeletion')); }
220
- }
221
- //2014
222
- static function hookUntrashedPosts() { add_action('untrash_post', array('WPPHEventWatcher', 'watchTrashUndo')); }
223
- // 2015
224
- static function hookUntrashedPages() { add_action('untrash_page', array('WPPHEventWatcher', 'watchTrashUndo')); }
225
- // 2016, 2017
226
- static function hookWatchPostStateBefore()
227
- {
228
- if(! isset($_POST)){ wpphLog(__METHOD__.' not $_POST method'); return; }
229
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { wpphLog(__METHOD__.' doing autosave'); return; }
230
- if(isset($_POST['action']) && $_POST['action'] == 'autosave') { wpphLog(__METHOD__.' $_POST action == autosave'); return; }
231
-
232
- if(isset($GLOBALS['WPPH_DEFAULT_EDITOR_ENABLED']) || isset($GLOBALS['WPPH_SCREEN_EDITOR_ENABLED']))
233
- {
234
- wpphLog(__METHOD__.'() triggered by hook.');
235
-
236
- global $wpdb;
237
- $pid = $_POST['post_ID'];
238
-
239
- $postType = (empty($_POST['post_type']) ? '' : $_POST['post_type']);
240
- if(! WPPHPost::validatePostType($postType)){
241
- wpphLog('Invalid post type.', array('post-type'=>$postType));
242
- return;
243
- }
244
-
245
- $_postType = $postType;
246
- if($_postType != 'post' && $_postType != 'page'){
247
- $_postType = 'custom';
248
- }
249
- do_action('wpph_set_post_type',$_postType);
250
-
251
- /*
252
- * CHECK IF POST/PAGE AUTHOR UPDATED; 2019
253
- * ## step 1: this is where we check if author has been changed
254
- * ## step 2: @see WPPHPost::managePostAuthorUpdateQuickEditForm()
255
- */
256
- if(! empty($_POST['post_author']))
257
- {
258
- $GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID'] = intval($_POST['post_author']);
259
- if(isset($GLOBALS['WPPH_SCREEN_EDITOR_ENABLED'])){
260
- // trigger hook manually
261
- add_filter('wp_insert_post_data', array('WPPHPost','managePostAuthorUpdateQuickEditForm'), 1, 2);
262
- }
263
- }
264
-
265
- // check if post exists & get aggregated data
266
- $query = "SELECT ID, post_title, post_name, post_password, post_date FROM ".$wpdb->posts." WHERE ID = ".$pid;
267
- $result = $wpdb->get_row($query);
268
- $postExists = (empty($result->ID) ? false : true);
269
- $GLOBALS['WPPH_POST_EXISTS'] = $postExists;
270
- $GLOBALS['WPPH_POST_PWD_PROTECTED'] = (empty($result->post_password) ? false : true);
271
-
272
- // if blog post
273
- if($postType == 'post' || $_postType == 'custom')
274
- {
275
- // before further checks, we have to make sure this post isn't new
276
- if(! $postExists){
277
- wpphLog("POST DOES NOT EXISTS.");
278
- return;
279
- }
280
-
281
- // retrieve the old post pwd to help us detect the posts' visibility transition state
282
- $GLOBALS['WPPH_OLD_POST_PASSWORD'] = $result->post_password;
283
- // check if post date has been changed
284
- $GLOBALS['WPPH_POST_OLD_DATE'] = $result->post_date;
285
- // Get the post name so we'll know if URL was updated
286
- $GLOBALS['WPPH_POST_OLD_NAME'] = (empty($result->post_name) ? $result->post_title : $result->post_name);
287
- // CHECK IF POST CATEGORY UPDATED; 2016
288
- $GLOBALS['WPPH_POST_OLD_CATEGORIES'] = wp_get_post_categories($pid);
289
- /*
290
- * CHECK IF POST URL UPDATED; 2017
291
- * ## step 1: this is where we retrieve the new URL
292
- * ## step 2: @see WPPHEventWatcher::watchBlogActivity()
293
- */
294
- $GLOBALS['WPPH_POST_NEW_URL'] = get_permalink($pid);
295
- }
296
- // if page
297
- elseif($postType == 'page')
298
- {
299
- if($postExists)
300
- {
301
- // get the page's password if any (to trigger the 2026 event)
302
- // retrieve the old post pwd to help us detect the posts' visibility transition state
303
- $GLOBALS['WPPH_OLD_POST_PASSWORD'] = $result->post_password;
304
- // check if post date has been changed
305
- $GLOBALS['WPPH_POST_OLD_DATE'] = $result->post_date;
306
- // Get the post name so we'll know if URL was updated
307
- $GLOBALS['WPPH_POST_OLD_NAME'] = (empty($result->post_name) ? $result->post_title : $result->post_name);
308
- }
309
-
310
- /*
311
- * CHECK IF PAGE URL UPDATED; 2018
312
- * ## step 1: this is where we retrieve the new URL
313
- * ## step 2: @see WPPHEventWatcher::watchBlogActivity()
314
- */
315
- $GLOBALS['WPPH_POST_NEW_URL'] = get_permalink($pid);
316
- }
317
- wpphLog('GLOBAL VARIABLES', array(
318
- 'WPPH_POST_AUTHOR_UPDATED_ID' => $GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID'],
319
- 'WPPH_POST_EXISTS' => $GLOBALS['WPPH_POST_EXISTS'],
320
- 'WPPH_POST_PWD_PROTECTED' => $GLOBALS['WPPH_POST_PWD_PROTECTED'],
321
- 'WPPH_OLD_POST_PASSWORD' => $GLOBALS['WPPH_OLD_POST_PASSWORD'],
322
- 'WPPH_POST_OLD_DATE' => $GLOBALS['WPPH_POST_OLD_DATE'],
323
- 'WPPH_POST_OLD_NAME' => $GLOBALS['WPPH_POST_OLD_NAME'],
324
- 'WPPH_POST_OLD_CATEGORIES' => isset($GLOBALS['WPPH_POST_OLD_CATEGORIES']) ? $GLOBALS['WPPH_POST_OLD_CATEGORIES'] : '',
325
- 'WPPH_POST_NEW_URL' => $GLOBALS['WPPH_POST_NEW_URL'],
326
- 'post_type' => $postType,
327
- 'WPPH_DEFAULT_EDITOR_ENABLED' => isset($GLOBALS['WPPH_DEFAULT_EDITOR_ENABLED']) ? 'true' : 'false',
328
- 'WPPH_SCREEN_EDITOR_ENABLED' => isset($GLOBALS['WPPH_SCREEN_EDITOR_ENABLED']) ? 'true' : 'false',
329
- )
330
- );
331
- }
332
- }
333
-
334
- // 2023
335
- static function hookWatchCategoryAdd() { WPPHEventWatcher::watchCategoryAdd($_POST); }
336
- // 2024
337
- static function hookWatchCategoryDelete() { WPPHEventWatcher::watchCategoryDelete($_POST); }
338
-
339
- // 3xxx - Themes management
340
-
341
- // 3000
342
- static function hookThemeChange() { add_action('switch_theme', array('WPPHEventWatcher', 'watchThemeChange'));}
343
-
344
-
345
- // 4xxx - User profile events
346
-
347
- // 4000, 4001, 4012
348
- static function hookUserRegisterEvent()
349
- {
350
- if(WPPH::isMultisite()){
351
- // 4012
352
- add_action('user_register', array('WPPHEventWatcher', 'watchWpmuUserRegister'));
353
- }
354
- // 4000 & 4001
355
- else { add_action('user_register', array('WPPHEventWatcher', 'watchEventUserRegister')); }
356
- }
357
- // 4002
358
- static function hookUserRoleUpdated() {
359
- add_action('edit_user_profile_update', array('WPPHEventWatcher', 'watchUserInfoUpdated'));
360
- add_action('personal_options_update', array('WPPHEventWatcher', 'watchUserInfoUpdated'));
361
- }
362
- // 4003, 4004
363
- static function hookUserPasswordUpdated() {
364
- add_action('edit_user_profile_update', array('WPPHEventWatcher', 'watchUserInfoUpdated'));
365
- add_action('personal_options_update', array('WPPHEventWatcher', 'watchUserInfoUpdated'));
366
- }
367
- // 4005, 4006
368
- static function hookUserEmailUpdated() {
369
- add_action('edit_user_profile_update', array('WPPHEventWatcher', 'watchUserInfoUpdated'));
370
- add_action('personal_options_update', array('WPPHEventWatcher', 'watchUserInfoUpdated'));
371
- }
372
- // 4008
373
- static function hookUserAdminPriv() {
374
- add_action('edit_user_profile_update', array('WPPHEventWatcher', 'watchUserAdminPrivUpdated'));
375
- add_action('personal_options_update', array('WPPHEventWatcher', 'watchUserAdminPrivUpdated'));
376
- }
377
-
378
- // 4007
379
- static function hookUserDeletion() { add_action( 'delete_user', array('WPPHEventWatcher', 'watchUserDeletion') ); }
380
-
381
-
382
- // 5xxx - Plugin management
383
-
384
- // 5000, 5001, 5002, 5003, 5004
385
- static function hookWatchPluginActivity() {
386
- @include_once(ABSPATH.'wp-admin/includes/plugin.php');
387
- WPPHEventWatcher::watchPluginInstall(); // 5000
388
- WPPHEventWatcher::watchPluginActivate(); // 5001
389
- WPPHEventWatcher::watchPluginDeactivate(); // 5002
390
- WPPHEventWatcher::watchPluginUninstall(); // 5003
391
- WPPHEventWatcher::watchPluginUpgrade(); // 5004
392
- }
393
-
394
-
395
- // 6xxx - System events
396
-
397
-
398
- // Events: 6001, 6002 are not available in MultiSite.
399
- // 6001, 6002, 6003
400
- static function hookCheckWpGeneralSettings(){
401
- if(WPPH::isMultisite())
402
- {
403
- if(isset($_POST))
404
- {
405
- if(isset($_POST['_wp_http_referer']) && !empty($_POST['_wp_http_referer'])){
406
- $wp_referrer = $_POST['_wp_http_referer'];
407
- if(false === ($pos = stripos($wp_referrer,'settings.php'))){
408
- return;
409
- }
410
- // 6003
411
- if(! empty($_POST['admin_email'])){
412
- $from = get_option('admin_email');
413
- $to = trim($_POST['admin_email']);
414
- if(strcasecmp($from,$to)!=0){
415
- wpphLog('Admin email changed',array(
416
- 'from' => $from,
417
- 'to' => $to
418
- ));
419
- self::_addLogEvent(6003, wp_get_current_user()->ID, WPPHUtil::getIP(), array($from, $to));
420
- }
421
- }
422
- }
423
- }
424
- return;
425
- }
426
- if(isset($_POST))
427
- {
428
- $wpphOptData = get_option(WPPH_USERS_CAN_REGISTER_OPT_NAME);
429
-
430
- // 6001
431
- if(!empty($_POST['option_page']) && $_POST['option_page'] == 'general')
432
- {
433
- if(isset($_POST['users_can_register'])){
434
- // on
435
- if(false === $wpphOptData || 0 == $wpphOptData){
436
- self::_addLogEvent(6001, wp_get_current_user()->ID, WPPHUtil::getIP(), array(__('Enabled')));
437
- update_option(WPPH_USERS_CAN_REGISTER_OPT_NAME,1);
438
- }
439
- }
440
- else {
441
- // off
442
- if(false === $wpphOptData || 1 == $wpphOptData){
443
- self::_addLogEvent(6001, wp_get_current_user()->ID, WPPHUtil::getIP(), array(__('Disabled')));
444
- update_option('wpph_users_can_register',0);
445
- }
446
- }
447
-
448
- // 6002
449
- if(! empty($_POST['default_role'])){
450
- $from = get_option('default_role');
451
- $to = trim($_POST['default_role']);
452
- if(strcasecmp($from,$to)!=0){
453
- wpphLog('Default user role changed',array(
454
- 'from' => $from,
455
- 'to' => $to
456
- ));
457
- self::_addLogEvent(6002, wp_get_current_user()->ID, WPPHUtil::getIP(), array($from, $to));
458
- }
459
- }
460
-
461
- // 6003
462
- if(! empty($_POST['admin_email'])){
463
- $from = get_option('admin_email');
464
- $to = trim($_POST['admin_email']);
465
- if(strcasecmp($from,$to)!=0){
466
- wpphLog('Admin email changed',array(
467
- 'from' => $from,
468
- 'to' => $to
469
- ));
470
- self::_addLogEvent(6003, wp_get_current_user()->ID, WPPHUtil::getIP(), array($from, $to));
471
- }
472
- }
473
- }
474
- }
475
- }
476
-
477
-
478
-
479
- /**
480
- * Add log event. Internal function. Do not use outside class scope.
481
- * @internal
482
- * @static
483
- * @param int $eventID
484
- * @param int $userID . A value of 0 means user "System". This is the ID of the user triggering the alert.
485
- * @param string $userIP
486
- * @param array $eventData Optional. If provided should be as an array.
487
- * @param string $failedLoginUserName The name of the user used for the failed login
488
- * @param int $blogID The blog id for which the event is triggered. If omitted, the global $blog_id variable will be used.
489
- * @return bool
490
- */
491
- static function _addLogEvent($eventID = 1000, $userID = 0, $userIP = '', $eventData = array(), $failedLoginUserName='', $blogID = null)
492
- {
493
- $params = func_get_args();
494
- wpphLog(__METHOD__.'() called with params:', $params);
495
- wpphLog(__METHOD__.'() triggered.');
496
- if(empty($blogID)){
497
- wpphLog('The blog ID was not provided. Trying to use the global $blog_id');
498
- global $blog_id; // try to get the current blog id if none provided
499
- if(empty($blog_id)){
500
- wpphLog('The blog ID could not be determined. Ignoring request for adding the log event.');
501
- return true;
502
- }
503
- }
504
- else { $blog_id = $blogID; }
505
-
506
- if(! wpph_isEventEnabled($eventID)){
507
- wpphLog('Event '.$eventID.' is not enabled. Ignoring request.');
508
- return true;
509
- }
510
-
511
- if(empty($userIP)){ $userIP = WPPHUtil::getIP(); }
512
- $tableName = WPPHDB::getFullTableName('MAIN');
513
- $eventData = base64_encode(serialize($eventData));
514
- $query = sprintf("INSERT INTO $tableName (EventID, UserID, UserIP, EventData, UserName, BlogId) VALUES(%d, %d, '%s', '%s', '%s', %d)",$eventID, $userID, $userIP, $eventData, $failedLoginUserName,$blog_id);
515
-
516
- global $wpdb;
517
- if($eventID == 1002){ // 1002 == failed login
518
-
519
- // check if there is already an event there
520
- $eventNumber = $wpdb->get_var("SELECT EventNumber FROM $tableName WHERE EventID = $eventID AND UserIP = '$userIP' AND UserName ='$failedLoginUserName'");
521
- if(! empty($eventNumber))
522
- {
523
- // update
524
- $query = "UPDATE $tableName
525
- SET
526
- EventDate = CURRENT_TIMESTAMP(),
527
- EventCount = (EventCount + 1)
528
- WHERE EventNumber = ".$eventNumber;
529
- }
530
- }
531
- return ((false === $wpdb->query($query)) ? false : true);
532
- }
533
-
534
-
535
- /*
536
- * PUBLIC METHODS
537
- * ============================================
538
- */
539
-
540
- static function getEventDetailsData($eventID)
541
- {
542
- global $wpdb;
543
- $table = WPPHDatabase::getFullTableName('events');
544
- return $wpdb->get_row("SELECT EventType, EventDescription FROM $table WHERE EventID = $eventID");
545
- }
546
-
547
- /**
548
- * Retrieve the events from db.
549
- * @param string $orderBy. Must be a valid column name. Defaults to EventNumber
550
- * @param string $sort ASC or DESC
551
- * @param array $limit
552
- * @param integer $blogId
553
- * @return mixed
554
- */
555
- static function getEvents($orderBy='EventNumber', $sort = 'DESC', $limit = array(0,0), $blogId = 1)
556
- {
557
- $validArgsSort = array('ASC', 'DESC');
558
- $validCnTableLogDetails = array('EventID', 'EventType');
559
- $validCnTableLog = array('EventNumber', 'EventDate', 'UserID', 'UserIP');
560
-
561
- $l0 = 0;
562
- $l1 = 1;
563
- if(isset($limit[0]) && ($limit[0] >= 0)){ $l0 = intval($limit[0]); }
564
- if(isset($limit[1]) && ($limit[1] >= 1)){ $l1 = intval($limit[1]); }
565
- $limit = "$l0,$l1";
566
-
567
- $sort = strtoupper($sort);
568
- if(empty($sort) || !in_array($sort, $validArgsSort)) { $sort = $validArgsSort[1]; }
569
-
570
- if(! empty($orderBy)){
571
- if(in_array($orderBy, $validCnTableLog)){
572
- $orderBy = 'le.'.$orderBy;
573
- }
574
- elseif(in_array($orderBy, $validCnTableLogDetails)){
575
- $orderBy = 'led.'.$orderBy;
576
- }
577
- }
578
- else { $orderBy = 'le.EventNumber'; }
579
-
580
- $t1 = WPPHDatabase::getFullTableName('main');
581
- $t2 = WPPHDatabase::getFullTableName('events');
582
- global $wpdb;
583
- $query = "SELECT le.EventNumber, le.EventID, le.EventDate, le.UserID, le.UserIP, le.EventData, le.EventCount, le.UserName, le.BlogId,
584
- led.EventType, led.EventDescription
585
- FROM `$t1` as le
586
- INNER JOIN `$t2` as led
587
- ON le.EventID = led.EventID
588
- WHERE ($blogId = 0) OR (le.BlogId = $blogId)
589
- ORDER BY $orderBy
590
- $sort
591
- LIMIT $limit;";
592
- return $wpdb->get_results($query, ARRAY_A);
593
- }
594
-
595
- }
596
-
597
- /**
598
- * Class WPPHEventWatcher
599
- * This class provides callable methods that are called inside the hooks registered
600
- * in the WPPHEvent class. All methods are internal and should not be used outside
601
- * scope.
602
- * @static
603
- * @internal
604
- */
605
- class WPPHEventWatcher extends WPPHEvent
606
- {
607
- /**
608
- * @internal
609
- * Hooks to the login event
610
- * @param $user_login
611
- * @param WP_User $user
612
- */
613
- static function watchEventLogin($user_login, $user)
614
- {
615
- wpphLog(__METHOD__.'() triggered by hook.');
616
- self::_addLogEvent(1000, $user->ID);
617
- }
618
- /**
619
- * @internal
620
- * Hooks to the logout event
621
- */
622
- static function watchEventLogout()
623
- {
624
- wpphLog(__METHOD__.'() triggered by hook.');
625
- self::_addLogEvent(1001, wp_get_current_user()->ID);
626
- }
627
-
628
-
629
- /**
630
- * @internal
631
- * Hooks to the user register event
632
- */
633
- static function watchEventUserRegister($user_id)
634
- {
635
- wpphLog(__METHOD__.'() triggered by hook.');
636
-
637
- global $current_user;
638
- get_currentuserinfo();
639
-
640
- $un = (empty($current_user->user_login) ? 'System' : $current_user->user_login);
641
- $uInfo = WPPHDB::getUserInfo($user_id);
642
- $nu = $uInfo['userName'];
643
- $nur = ucfirst($uInfo['userRole']);
644
-
645
- wpphLog(__METHOD__.'() -> USER INFO', array('user_id'=>$user_id, 'current_user'=>$current_user, 'user_info'=>$uInfo));
646
-
647
- if($un == 'System')
648
- {
649
- // A new user with the username %username% has registered with the role of %user_role%
650
- $eventData = array($nu, $nur);
651
- self::_addLogEvent(4000, 0, WPPHUtil::getIP(), $eventData);
652
- }
653
- else {
654
- // %s created new user %s with role %s
655
- $eventData = array($un, $nu, $nur);
656
- self::_addLogEvent(4001, $current_user->ID, WPPHUtil::getIP(), $eventData);
657
- }
658
- }
659
-
660
- /**
661
- * 6000
662
- * @internal
663
- * Hooks to the events deletion event
664
- */
665
- static function __deleteEvents()
666
- {
667
- wpphLog(__METHOD__.'() triggered.');
668
-
669
- // check settings and delete the events (if any)
670
- $settings = WPPH::getPluginSettings();
671
-
672
- $runCleanup = ((time() - $settings->lastCleanup) < WPPH_CLEANUP_WAIT_TIME);
673
- if(! $runCleanup){
674
- wpphLog(__METHOD__.'() Ignored. Not enough time elapsed between deletion requests.');
675
- return;
676
- }
677
-
678
- // check to see how we should do the cleanup (by days or by number)
679
- $cleanupType = 1; // by number by default
680
-
681
- if(!empty($settings->daysToKeep)){
682
- $cleanupType = 0;
683
- }
684
-
685
- // by days
686
- if($cleanupType == 0)
687
- {
688
- if(self::_deleteEventsOlderThan($settings->daysToKeep)){
689
- $settings->cleanupRan = 1;
690
- $settings->lastCleanup = time();
691
- self::_addLogEvent(6000, 0);
692
- wpphLog(__METHOD__.'() Cleanup complete.');
693
- }
694
- }
695
- // by number
696
- else
697
- {
698
- if(self::_deleteEventsGreaterThan($settings->eventsToKeep)){
699
- $settings->cleanupRan = 1;
700
- $settings->lastCleanup = time();
701
- self::_addLogEvent(6000, 0);
702
- wpphLog(__METHOD__.'() Cleanup complete.');
703
- }
704
- }
705
- WPPH::updatePluginSettings($settings);
706
- }
707
-
708
- //@internal
709
- // delete by days
710
- private static function _deleteEventsOlderThan($days = 1)
711
- {
712
- global $wpdb;
713
-
714
- // run for each blog
715
- if(WPPH::isMultisite())
716
- {
717
- $old_blog = $wpdb->blogid;
718
- $blogIds = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
719
- foreach ($blogIds as $blog_id)
720
- {
721
- switch_to_blog($blog_id);
722
- $query = sprintf("DELETE FROM ".WPPHDatabase::getFullTableName('main')." WHERE BlogID=%d EventDate < (NOW() - INTERVAL %d DAY)",$blog_id, $days);
723
- $result = $wpdb->query($query);
724
- if($result === false){ $status = 'Error executing query'; }
725
- else { $status = 'Query executed'; }
726
- wpphLog(__METHOD__.'('.$days.') called for blog: '.$blog_id.'.', array('query'=>$query, 'status'=>$status, 'rowsDeleted'=> (int)$result));
727
- }
728
- switch_to_blog($old_blog);
729
- return true;
730
- }
731
- else {
732
- $query = sprintf("DELETE FROM ".WPPHDatabase::getFullTableName('main')." WHERE EventDate < (NOW() - INTERVAL %d DAY)", $days);
733
- $result = $wpdb->query($query);
734
- if($result === false){ $status = 'Error executing query'; }
735
- else { $status = 'Query executed'; }
736
- wpphLog(__METHOD__.'('.$days.') called.', array('query'=>$query, 'status'=>$status, 'rowsDeleted'=> (int)$result));
737
- return ($result !== false);
738
- }
739
-
740
- }
741
- //@internal
742
- // delete by number
743
- private static function _deleteEventsGreaterThan($number = WPPH_KEEP_MAX_EVENTS)
744
- {
745
- global $wpdb;
746
- if($number > WPPH_KEEP_MAX_EVENTS){ $number = WPPH_KEEP_MAX_EVENTS; }
747
- $tableName = WPPHDatabase::getFullTableName('main');
748
-
749
- // run for each blog
750
- if(WPPH::isMultisite())
751
- {
752
- $old_blog = $wpdb->blogid;
753
- $blogIds = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
754
- foreach ($blogIds as $blog_id)
755
- {
756
- switch_to_blog($blog_id);
757
- $count = $wpdb->get_var(sprintf("SELECT COUNT(0) FROM $tableName WHERE BlogID=%d",$blog_id));
758
- if(empty($count)){
759
- wpphLog(__METHOD__.'('.$number.') called for blog id: '.$blog_id.'. Ignored, there are no events for this blog id in the database.');
760
- continue;
761
- }
762
- $keep = $number;
763
- if($count > $keep)
764
- {
765
- $limit = $count - $keep;
766
- $query = sprintf("DELETE FROM $tableName WHERE BlogID=%d ORDER BY EventDate LIMIT %d", $blog_id, $limit);
767
- $result = $wpdb->query($query);
768
- if($result === false){ $status = 'Error executing query'; }
769
- else { $status = 'Query executed'; }
770
- wpphLog(__METHOD__.'('.$number.') called for blog id: '.$blog_id.'.', array('query'=>$query, 'status'=>$status, 'rowsAffected'=> (int)$result));
771
- return ($result !== false);
772
- }
773
- else {
774
- wpphLog(__METHOD__.'('.$number.') called for blog id: '.$blog_id.'. Ignored, there are not enough events to perform this action.');
775
- continue;
776
- }
777
- }
778
- switch_to_blog($old_blog);
779
- return true;
780
- }
781
- else {
782
- $count = $wpdb->get_var("SELECT COUNT(0) FROM $tableName");
783
- if(empty($count)){
784
- wpphLog(__METHOD__.'('.$number.') called. Ignored, there are no events in the database');
785
- return true;
786
- }
787
- $keep = $number;
788
- if($count > $keep)
789
- {
790
- $limit = $count - $keep;
791
- $query = "DELETE FROM $tableName ORDER BY EventDate LIMIT $limit";
792
- $result = $wpdb->query($query);
793
- if($result === false){ $status = 'Error executing query'; }
794
- else { $status = 'Query executed'; }
795
- wpphLog(__METHOD__.'('.$number.') called.', array('query'=>$query, 'status'=>$status, 'rowsAffected'=> (int)$result));
796
- return ($result !== false);
797
- }
798
- else {
799
- wpphLog(__METHOD__.'('.$number.') called. Ignored, there are not enough events to trigger this action.');
800
- return true;
801
- }
802
- }
803
- }
804
-
805
- /**
806
- * @internal
807
- * Fired on login failure
808
- */
809
- static function watchLoginFailure($username='')
810
- {
811
- wpphLog(__METHOD__.'() triggered by hook.', array('username'=>$username));
812
- self::_addLogEvent(1002,0,WPPHUtil::getIP(),array($username), base64_encode($username));
813
- }
814
-
815
- static function watchUserInfoUpdated($userID)
816
- {
817
- wpphLog(__METHOD__.'() triggered by hook.', array('POST DATA'=>$_POST));
818
-
819
- // get info for the currently logged in user
820
- $current_user = wp_get_current_user();
821
- $cid = $current_user->ID;
822
- $cName = $current_user->user_login;
823
-
824
- // get info for the currently updated user
825
- $editedUserInfo = WPPHDB::getUserInfo($userID);
826
- $editedUserName = $editedUserInfo['userName'];
827
- $initialUserRole = $editedUserInfo['userRole'];
828
-
829
- // check and see *what* has been updated...
830
-
831
- // If a user's role has been updated
832
- if(!empty($_POST['role'])){
833
- $updatedRole = trim($_POST['role']);
834
- if(self::_userRoleUpdated($cid, $initialUserRole, $updatedRole, $editedUserName, $cName)){
835
- //return;
836
- }
837
- }
838
-
839
- // If a user's password has been updated
840
- if(!empty($_POST['pass1'])){
841
- if(self::_userPasswordUpdated($userID, $cid, $cName, $editedUserName, $initialUserRole)){
842
- // return;
843
- }
844
- }
845
-
846
- // if a user's email was updated
847
- if(! empty($_POST['email'])){
848
- global $wpdb;
849
- // get current user's email
850
- $oldEmail = $wpdb->get_var("SELECT user_email FROM ".$wpdb->users." WHERE ID = $userID");
851
- // new email
852
- $newEmailSafe = mysql_real_escape_string($_POST['email']);
853
- self::_userEmailUpdated($userID, $cid, $cName, $oldEmail, $newEmailSafe);
854
- }
855
- }
856
-
857
- /**
858
- * @internal
859
- * Triggered when a user's role is updated
860
- * @param $currentUserID
861
- * @param $initialUserRole
862
- * @param $updatedRole
863
- * @param $editedUserName
864
- * @param $currentUserName
865
- * @return bool
866
- */
867
- private static function _userRoleUpdated($currentUserID, $initialUserRole, $updatedRole, $editedUserName, $currentUserName)
868
- {
869
- wpphLog(__METHOD__.'() triggered by hook.');
870
-
871
- if(strcasecmp($initialUserRole, $updatedRole)==0){
872
- wpphLog(__METHOD__.'() Ignored. Role did not change.');
873
- return false;
874
- }
875
-
876
- // The role of user <strong>%s</strong> was changed from <strong>%s</strong> to <strong>%s</strong> by <strong>%s</strong>
877
- $eData = array($editedUserName, ucfirst($initialUserRole), ucfirst($updatedRole), $currentUserName);
878
-
879
- self::_addLogEvent(4002, $currentUserID, WPPHUtil::getIP(), $eData);
880
- return true;
881
- }
882
-
883
- /**
884
- * @internal
885
- * Triggered when a user's role is updated
886
- * @param $userID
887
- * @param $currentUserID
888
- * @param $currentUserName
889
- * @param $editedUserName
890
- * @param $editedUserRole
891
- * @return bool
892
- */
893
- private static function _userPasswordUpdated($userID, $currentUserID, $currentUserName, $editedUserName, $editedUserRole)
894
- {
895
- wpphLog(__METHOD__.'() triggered by hook.');
896
-
897
- // check to see who's who here
898
- if($userID == $currentUserID)
899
- {
900
- self::_addLogEvent(4003, $currentUserID);
901
- return true;
902
- }
903
-
904
- $eData = array($currentUserName, $editedUserName, ucfirst($editedUserRole));
905
- self::_addLogEvent(4004, $currentUserID, WPPHUtil::getIP(), $eData);
906
- return true;
907
- }
908
-
909
- /**
910
- * @internal
911
- * Triggered when a user's email is updated
912
- * @param $userID The user ID triggering this hook
913
- * @param $currentUserID The ID of the current logged in user
914
- * @param $currentUserName
915
- * @param $oldEmail
916
- * @param $newEmail
917
- * @return bool
918
- */
919
- private static function _userEmailUpdated($userID, $currentUserID, $currentUserName, $oldEmail, $newEmail)
920
- {
921
- wpphLog(__METHOD__.'() triggered by hook.');
922
-
923
- // check if email updated
924
- if($newEmail == $oldEmail){
925
- wpphLog(__METHOD__.'() Ignored. Email did not change.');
926
- return false;
927
- }
928
-
929
- // check to see who's who here
930
- if($userID == $currentUserID)
931
- {
932
- self::_addLogEvent(4005, $currentUserID, WPPHUtil::getIP(), array($oldEmail, $newEmail));
933
- return true;
934
- }
935
-
936
- // get info for the currently updated user
937
- $editedUserInfo = WPPHDB::getUserInfo($userID);
938
- $editedUserName = $editedUserInfo['userName'];
939
-
940
- // %user_making_change% changed the email address of user account %user% from %old_email% to %new_email%
941
- $eData = array($currentUserName, $editedUserName, $oldEmail, $newEmail);
942
-
943
- self::_addLogEvent(4006, $currentUserID, WPPHUtil::getIP(), $eData);
944
- return true;
945
- }
946
-
947
- /**
948
- * @internal
949
- * @param $userID the id of the user being deleted
950
- * Triggered when a user is deleted
951
- */
952
- static function watchUserDeletion($userID)
953
- {
954
- wpphLog(__METHOD__.'() triggered by hook.');
955
-
956
- global $current_user;
957
- get_currentuserinfo();
958
-
959
- $un = (empty($current_user->user_login) ? 'System' : $current_user->user_login);
960
- if($un == 'System'){
961
- $currentUserID = 0;
962
- }
963
- else { $currentUserID = $current_user->ID; }
964
-
965
- // get info for the currently deleted user
966
- $_userInfo = WPPHDB::getUserInfo($userID);
967
- $_userName = $_userInfo['userName'];
968
- $_userRole = ucfirst($_userInfo['userRole']);
969
-
970
- self::_addLogEvent(4007, $currentUserID, WPPHUtil::getIP(), array($_userName, $_userRole, $un));
971
- }
972
-
973
- // # 5001
974
- static function watchPluginActivate()
975
- {
976
- wpphLog(__METHOD__.'() triggered by hook.');
977
-
978
- $userID = wp_get_current_user()->ID;
979
- $userIP = WPPHUtil::getIP();
980
-
981
- // activate one by link
982
- if(isset($_GET['action']) && ($_GET['action']=='activate') || isset($_GET['action2']) && ($_GET['action2']=='activate'))
983
- {
984
- wpphLog('------------------------------- 1 ------------------------------');
985
- $pluginFile = isset($_GET['plugin']) ? $_GET['plugin'] : null;
986
- if(empty($pluginFile)){
987
- wpphLog("EMPTY plugin param in GET");
988
- return;
989
- }
990
- $fp = WP_PLUGIN_DIR.'/'.$pluginFile;
991
- if(! is_file($fp)){
992
- wpphLog("Plugin not found", array('filePath'=>$fp));
993
- return;
994
- }
995
- $pluginData = get_plugin_data($fp,false,false);
996
- self::_addLogEvent(5001, $userID, $userIP, array($pluginData['Name'],$pluginFile));
997
- wpphLog('Plugin activated.', array('plugin file'=>$pluginFile));
998
- return;
999
- }
1000
- // one by bulk
1001
- elseif(isset($_POST['action']) && ($_POST['action'] == 'activate-selected') || (isset($_POST['action2']) && ($_POST['action2'] == 'activate-selected')))
1002
- {
1003
- wpphLog('------------------------------- 2 ------------------------------');
1004
- if(! empty($_POST['checked']))
1005
- {
1006
- foreach($_POST['checked'] as $k=>$pluginFile)
1007
- {
1008
- if(empty($pluginFile)){
1009
- wpphLog("EMPTY plugin param in POST");
1010
- return;
1011
- }
1012
- $fp = WP_PLUGIN_DIR.'/'.$pluginFile;
1013
- if(! is_file($fp)){
1014
- wpphLog("Plugin not found", array('filePath'=>$fp));
1015
- return;
1016
- }
1017
- $pluginData = get_plugin_data($fp,false,false);
1018
- self::_addLogEvent(5001, $userID, $userIP, array($pluginData['Name'],$pluginFile));
1019
- wpphLog('Plugin activated.', array('plugin file'=>$pluginFile));
1020
- }
1021
- }
1022
- }
1023
- // more by bulk
1024
- elseif(isset($_POST['activate-multi']) && ($_POST['action']=='activate-selected' || (isset($_POST['action2']) && $_POST['action2']=='activate-selected')))
1025
- {
1026
- wpphLog('------------------------------- 3 ------------------------------');
1027
- if(! empty($_POST['checked']))
1028
- {
1029
- foreach($_POST['checked'] as $k=>$pluginFile)
1030
- {
1031
- if(empty($pluginFile)){
1032
- wpphLog("EMPTY plugin param in POST");
1033
- return;
1034
- }
1035
- $fp = WP_PLUGIN_DIR.'/'.$pluginFile;
1036
- if(! is_file($fp)){
1037
- wpphLog("Plugin not found", array('filePath'=>$fp));
1038
- return;
1039
- }
1040
- $pluginData = get_plugin_data($fp,false,false);
1041
- self::_addLogEvent(5001, $userID, $userIP, array($pluginData['Name'],$pluginFile));
1042
- wpphLog('Plugin activated.', array('plugin file'=>$pluginFile));
1043
- }
1044
- }
1045
- }
1046
- }
1047
- // # 5002
1048
- static function watchPluginDeactivate()
1049
- {
1050
- wpphLog(__METHOD__.'() triggered by hook.');
1051
-
1052
- // get info for the currently logged in user
1053
- $userID = wp_get_current_user()->ID;
1054
- $userIP = WPPHUtil::getIP();
1055
-
1056
- // activate one by link
1057
- if((isset($_GET['action']) && $_GET['action']=='deactivate') || isset($_GET['action2']) && ($_GET['action2']=='deactivate'))
1058
- {
1059
- wpphLog('------------------------------- 1 ------------------------------');
1060
- $pluginFile = $_GET['plugin'];
1061
- $pluginData = get_plugin_data(WP_PLUGIN_DIR.'/'.$pluginFile,false,false);
1062
- $pluginName = $pluginData['Name'];
1063
- self::_addLogEvent(5002, $userID, $userIP, array($pluginName,$pluginFile));
1064
- wpphLog('Plugin deactivated.', array('plugin file'=>$pluginFile));
1065
- }
1066
- // one by bulk
1067
- elseif((isset($_POST['action']) && $_POST['action'] == 'deactivate-selected') || isset($_POST['action2']) && ($_POST['action2'] == 'deactivate-selected'))
1068
- {
1069
- if(! empty($_POST['checked']))
1070
- {
1071
- wpphLog('------------------------------- 2 ------------------------------');
1072
- foreach($_POST['checked'] as $k=>$pluginFile){
1073
- $pluginData = get_plugin_data(WP_PLUGIN_DIR.'/'.$pluginFile,false,false);
1074
- $pluginName = $pluginData['Name'];
1075
- self::_addLogEvent(5002, $userID, $userIP, array($pluginName,$pluginFile));
1076
- wpphLog('Plugin deactivated.', array('plugin file'=>$pluginFile));
1077
- }
1078
- }
1079
- }
1080
- // more by bulk
1081
- elseif(isset($_GET['deactivate-multi']) && (isset($_POST['action']) && $_POST['action']=='deactivate-selected' || (isset($_POST['action2']) && $_POST['action2']=='deactivate-selected')))
1082
- {
1083
- if(! empty($_POST['checked']))
1084
- {
1085
- wpphLog('------------------------------- 3 ------------------------------');
1086
- foreach($_POST['checked'] as $k=>$pluginFile){
1087
- $pluginData = get_plugin_data(WP_PLUGIN_DIR.'/'.$pluginFile,false,false);
1088
- $pluginName = $pluginData['Name'];
1089
- self::_addLogEvent(5002, $userID, $userIP, array($pluginName,$pluginFile));
1090
- wpphLog('Plugin deactivated.', array('plugin file'=>$pluginFile));
1091
- }
1092
- }
1093
- }
1094
- }
1095
-
1096
- // # 5000
1097
- static function watchPluginInstall()
1098
- {
1099
- if(defined('WPPH_PLUGIN_INSTALLED_OK')){ return; }
1100
- if(empty($_GET)) { return; }
1101
-
1102
- if(isset($_GET['action']) && $_GET['action']=='install-plugin'){
1103
- wpphLog(__METHOD__.'() triggered by hook.');
1104
- wpph_installPlugin($_GET['plugin'], wp_get_current_user()->ID, WPPHUtil::getIP());
1105
- }
1106
- elseif(isset($_GET['action2']) && $_GET['action2']=='install-plugin'){
1107
- wpphLog(__METHOD__.'() triggered by hook.');
1108
- wpph_installPlugin($_GET['plugin'], wp_get_current_user()->ID, WPPHUtil::getIP());
1109
- }
1110
- }
1111
- // # 5003
1112
- static function watchPluginUninstall()
1113
- {
1114
- if(empty($_POST)) { return; }
1115
- if(! isset($_POST['verify-delete'])) { return; }
1116
- if(empty($_POST['checked'])){ return; }
1117
-
1118
- $action = '';
1119
- if(! empty($_POST['action'])){ $action = $_POST['action'];}
1120
- elseif(! empty($_POST['action2'])){ $action = $_POST['action2'];}
1121
- if(empty($action) || $action != 'delete-selected') {
1122
- return;
1123
- }
1124
-
1125
- wpphLog(__METHOD__.'() triggered by hook.');
1126
- // get info for the currently logged in user
1127
- $current_user = wp_get_current_user();
1128
- foreach($_POST['checked'] as $pluginFile){
1129
- $pluginData = get_plugin_data(WP_PLUGIN_DIR.'/'.$pluginFile,false,false);
1130
- $pluginName = $pluginData['Name'];
1131
- self::_addLogEvent(5003,$current_user->ID, WPPHUtil::getIP(), array($pluginName,$pluginFile));
1132
- wpphLog('Plugin uninstalled.', array('plugin file'=>$pluginFile));
1133
- }
1134
- }
1135
- // # 5004
1136
- static function watchPluginUpgrade()
1137
- {
1138
- wpphLog(__METHOD__.'() triggered.');
1139
-
1140
- $current_user = wp_get_current_user();
1141
- $userID = $current_user->ID;
1142
- $ip = WPPHUtil::getIP();
1143
-
1144
- // One by link
1145
- if(!empty($_GET))
1146
- {
1147
- if(isset($_GET['action']) && !empty($_GET['action']))
1148
- {
1149
- $action = $_GET['action'];
1150
- if(! in_array($action, array('upgrade-plugin', 'update-selected'))){
1151
- return;
1152
- }
1153
- if(! empty($_GET['plugin'])){ $pluginFile = $_GET['plugin']; }
1154
- if(empty($pluginFile)){
1155
- return;
1156
- }
1157
- $pluginData = get_plugin_data(WP_PLUGIN_DIR.'/'.$pluginFile,false,false);
1158
- $pluginName = $pluginData['Name'];
1159
- // Upgraded the plugin <strong>%s</strong> installed in /<strong>%s</strong>
1160
- self::_addLogEvent(5004,$current_user->ID, WPPHUtil::getIP(), array($pluginName,$pluginFile));
1161
- wpphLog('Plugin upgraded.', array('plugin file'=>$pluginFile));
1162
- return;
1163
- }
1164
- }
1165
- elseif(isset($_POST))
1166
- {
1167
- $action = '';
1168
- if(isset($_POST['action']) && !empty($_POST['action'])){
1169
- $action = $_POST['action'];
1170
- if(! in_array($action, array('upgrade-plugin', 'update-selected'))){
1171
- $action = '';
1172
- if(isset($_POST['action2']) && !empty($_POST['action2'])){
1173
- $action = $_POST['action2'];
1174
- if(! in_array($action, array('upgrade-plugin', 'update-selected'))){
1175
- return;
1176
- }
1177
- }
1178
- }
1179
- }
1180
- if(empty($action)) { return; }
1181
-
1182
- if(! isset($_POST['checked']) || empty($_POST['checked'])){
1183
- return;
1184
- }
1185
- foreach($_POST['checked'] as $i=>$pluginFile){
1186
- $pluginData = get_plugin_data(WP_PLUGIN_DIR.'/'.$pluginFile,false,false);
1187
- $pluginName = $pluginData['Name'];
1188
- self::_addLogEvent(5004,$userID, $ip, array($pluginName,$pluginFile));
1189
- wpphLog('Plugin upgraded.', array('plugin file'=>$pluginFile));
1190
- }
1191
- }
1192
- }
1193
-
1194
-
1195
- static function watchBlogActivity($newStatus, $oldStatus, $post)
1196
- {
1197
- wpphLog(__METHOD__.'() triggered.');
1198
-
1199
- wpphLog(__METHOD__.'. POST STATUS DATA', array(
1200
- '$oldStatus' => $oldStatus,
1201
- '$newStatus' => $newStatus,
1202
- '$postStatus' => $post->post_status,
1203
- '$post' => $post
1204
- ));
1205
-
1206
- // IGNORE STATES - so we skip generating multiple events
1207
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { wpphLog('Doing autosave'); return; }
1208
- if(empty($post->post_type)){ wpphLog('Empty post->post_type'); return; }
1209
- if($post->post_type == 'revision') { wpphLog('Post type == revision.'); return; }
1210
- if($newStatus == 'auto-draft' || ($oldStatus == 'new' && $newStatus=='auto-draft')) { wpphLog('Doing draft autosave'); return; }
1211
-
1212
- $types = WPPHPost::getPostTypes();
1213
- if(! in_array($post->post_type, $types)){
1214
- wpphLog('Invalid post type.', array('post-type'=>$post->post_type));
1215
- return;
1216
- }
1217
- do_action('wpph_set_post_type','custom');
1218
-
1219
- $postID = $post->ID;
1220
- $postTitle = $post->post_title;
1221
- $postUrl = get_permalink($postID);
1222
- $postStatus = $post->post_status;
1223
- $currentUserID = wp_get_current_user()->ID;
1224
- $userID = $postAuthorID = (int)$post->post_author;
1225
- if($currentUserID != $postAuthorID){
1226
- // someone else is doing this
1227
- $userID = $currentUserID;
1228
- }
1229
-
1230
- // CHECK TO SEE IF THIS UPDATE IS FROM THE QUICK EDIT FORM or the default wp post editor
1231
- if(isset($_POST['original_post_status']))
1232
- {
1233
- $originalPostStatus = $_POST['original_post_status'];
1234
- }
1235
- else{
1236
- // quick edit form
1237
- $originalPostStatus = (isset($_POST['_status']) ? $_POST['_status'] : null);
1238
- }
1239
-
1240
- if(empty($originalPostStatus)){
1241
- // wpphLog(__METHOD__.' $_POST["_status"] not found. $originalPostStatus is EMPTY - nothing to do here.');
1242
- // wpphLog(__METHOD__.' POST DATA',$_POST);
1243
- return;
1244
- }
1245
-
1246
- $postTypePost = $postTypePage = $customPostType = false;
1247
-
1248
- if($post->post_type == 'post'){ $postTypePost = true;}
1249
- elseif($post->post_type == 'page'){ $postTypePage = true;}
1250
- else { $customPostType = true; }
1251
-
1252
- if(!$postTypePost && !$postTypePage && !$customPostType){
1253
- wpphLog('Ignored. Invalid post type', array('postType'=>$post->post_type));
1254
- return;
1255
- }
1256
-
1257
- WPPHPost::$currentPostType = $post->post_type;
1258
-
1259
- global $wpdb;
1260
-
1261
- //## 2025 & 2026 & 2040
1262
- if($customPostType){ self::watchPostVisibilityChange($oldStatus, $newStatus, $userID, $postTitle, $post, 2040); }
1263
- else { self::watchPostVisibilityChange($oldStatus, $newStatus, $userID, $postTitle, $post, ($postTypePost) ? 2025 : 2026); }
1264
-
1265
- //## 2027 & 2028 & 2041
1266
- if(! in_array($oldStatus, array('new', 'auto-draft'))){
1267
- if($customPostType){ self::watchPostDateChange($userID, $postTitle, $post->post_date, 2041);}
1268
- else { self::watchPostDateChange($userID, $postTitle, $post->post_date, ($postTypePost) ? 2027 : 2028); }
1269
- }
1270
-
1271
- //## 2016 & 2036
1272
- if($postTypePost){ self::watchPostCategoriesChange($post, $wpdb, $postTitle, 2016); }
1273
- elseif($customPostType){ self::watchPostCategoriesChange($post, $wpdb, $postTitle, 2036); }
1274
-
1275
- //## 2019 & 2020 & 2038
1276
- $authorChanged = false;
1277
- if(isset($GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID']))
1278
- {
1279
- $quickFormEnabled = isset($GLOBALS['WPPH_SCREEN_EDITOR_ENABLED']) ? true : false;
1280
- if($customPostType){
1281
- if(WPPHPost::postAuthorChanged((int)$GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID'], $postID, $userID, $postTitle, 2038, $quickFormEnabled)){
1282
- $GLOBALS['WPPH_POST_AUTHOR_UPDATED'] = true;
1283
- $authorChanged = true;
1284
- }
1285
- }
1286
- else {
1287
- if(WPPHPost::postAuthorChanged((int)$GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID'], $postID, $userID, $postTitle, ($postTypePost) ? 2019 : 2020, $quickFormEnabled)){
1288
- $GLOBALS['WPPH_POST_AUTHOR_UPDATED'] = true;
1289
- $authorChanged = true;
1290
- }
1291
- }
1292
- unset($GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID']);
1293
- }
1294
-
1295
- // 2000 & 2004 & 2029
1296
- if($newStatus != 'publish'){
1297
- if($originalPostStatus == 'auto-draft' || ($oldStatus=='new' && $newStatus=='inherit' && $postStatus=='inherit'))
1298
- {
1299
- if($customPostType){ WPPHPost::newPostAsDraft($userID, $postID, $postTitle, 2029); }
1300
- else { WPPHPost::newPostAsDraft($userID, $postID, $postTitle, ($postTypePost) ? 2000 : 2004); }
1301
- }
1302
- }
1303
-
1304
- // check if post/page modified
1305
- $postModified = self::watchPostChanged($wpdb, $postID);
1306
- wpphLog('POST MODIFIED',array('modified'=> $postModified ? 'true' : 'false'));
1307
-
1308
- //## 2000 & 2003 & 2004 & 2007 & 2032
1309
- if(($oldStatus == 'draft') && ($newStatus == 'draft' && $postStatus == 'draft'))
1310
- {
1311
- if($originalPostStatus == 'draft')
1312
- {
1313
- //## 2003 - draft post updated
1314
- if($postTypePost || $customPostType){
1315
- // only if 2016 || 2017 || 2019 were not triggered
1316
- if(isset($GLOBALS['WPPH_POST_CATEGORIES_UPDATED']) || isset($GLOBALS['WPPH_POST_URL_UPDATED']) || isset($GLOBALS['WPPH_POST_AUTHOR_UPDATED'])){}
1317
- else {
1318
- $event = ($customPostType ? 2032 : 2003);
1319
- WPPHPost::draftPostUpdated($userID, $postID, $postTitle, $event);
1320
- $postModified = false;
1321
- }
1322
- }
1323
- //## 2007 - draft page updated
1324
- else {
1325
- // only if 2018 || 2020 were not triggered
1326
- if(isset($GLOBALS['WPPH_PAGE_URL_UPDATED']) || isset($GLOBALS['WPPH_PAGE_AUTHOR_UPDATED'])){}
1327
- else {
1328
- WPPHPost::draftPostUpdated($userID, $postID, $postTitle, 2007);
1329
- $postModified = false;
1330
- }
1331
- }
1332
- }
1333
- }
1334
-
1335
- //## 2001 & 2005 & 2030 - new post/page published
1336
- elseif(in_array($oldStatus, array('draft','auto-draft','pending')) && $newStatus == 'publish' && $postStatus == 'publish')
1337
- {
1338
- $event = 0;
1339
- if($customPostType) { $event = 2030;}
1340
- elseif($postTypePost) { $event = 2001; }
1341
- elseif($postTypePage) { $event = 2005; }
1342
- if(! empty($event)){
1343
- WPPHPost::newPostPublished($userID, $postTitle, $postUrl, $event);
1344
- return; // no need to process further
1345
- }
1346
- }
1347
-
1348
- //## 2021 & 2022 & 2039 : published -> pending
1349
- elseif($oldStatus == 'publish' && $newStatus == 'pending' && $postStatus == 'pending')
1350
- {
1351
- $event = 0;
1352
- if($customPostType) { $event = 2039;}
1353
- elseif($postTypePost) { $event = 2021; }
1354
- elseif($postTypePage) { $event = 2022; }
1355
- if(! empty($event)){
1356
- WPPHPost::postStatusChanged($postTitle, __('Published'), __('Pending Review'), $userID, $event);
1357
- }
1358
- }
1359
-
1360
- //## 2021 & 2022 & 2039 : pending -> draft
1361
- elseif($oldStatus == 'pending' && $newStatus == 'draft' && $postStatus == 'draft')
1362
- {
1363
- $event = 0;
1364
- if($customPostType) { $event = 2039;}
1365
- elseif($postTypePost) { $event = 2021; }
1366
- elseif($postTypePage) { $event = 2022; }
1367
- if(! empty($event)){
1368
- WPPHPost::postStatusChanged($postTitle, __('Pending Review'), __('Draft'), $userID, $event);
1369
- }
1370
- }
1371
-
1372
- //## 2021 & 2022 & 2039 : draft -> pending
1373
- elseif($oldStatus == 'draft' && $newStatus == 'pending' && $postStatus == 'pending')
1374
- {
1375
- $event = 0;
1376
- if($customPostType) { $event = 2039;}
1377
- elseif($postTypePost) { $event = 2021; }
1378
- elseif($postTypePage) { $event = 2022; }
1379
- if(! empty($event)){
1380
- WPPHPost::postStatusChanged($postTitle, __('Draft'), __('Pending Review'), $userID, $event);
1381
- }
1382
- }
1383
-
1384
- //## 2021 & 2022 & 2039 : published -> draft
1385
- elseif($oldStatus == 'publish' && $newStatus == 'draft' && $postStatus == 'draft')
1386
- {
1387
- $event = 0;
1388
- if($customPostType) { $event = 2039;}
1389
- elseif($postTypePost) { $event = 2021; }
1390
- elseif($postTypePage) { $event = 2022; }
1391
- if(! empty($event)){
1392
- WPPHPost::postStatusChanged($postTitle, __('Published'), __('Draft'), $userID, $event);
1393
- }
1394
- }
1395
-
1396
- //## 2002 & 2006 & 2017 & 2018 & 2031 & 2037 : published post/page updated
1397
- elseif($oldStatus == 'publish' && $newStatus == 'publish' && $postStatus == 'publish')
1398
- {
1399
- // trigger: 2017 & 2018 & 2037 - Changed the URL of the post %post_name% from %old_url% to %new_url%
1400
- if(isset($GLOBALS['WPPH_POST_NEW_URL']) || $postModified)
1401
- {
1402
- $event = 0;
1403
- if($customPostType) { $event = 2037;}
1404
- elseif($postTypePost) { $event = 2017; }
1405
- elseif($postTypePage) { $event = 2018; }
1406
- if(! empty($event)){
1407
- if(WPPHPost::postUrlUpdated($GLOBALS['WPPH_POST_NEW_URL'], get_permalink($postID), $userID, $postTitle, $event))
1408
- {
1409
- unset($GLOBALS['WPPH_POST_NEW_URL']);
1410
- $GLOBALS['WPPH_POST_URL_UPDATED'] = $postTypePost;
1411
- $GLOBALS['WPPH_PAGE_URL_UPDATED'] = $postTypePage;
1412
- }
1413
- }
1414
- }
1415
- // 2002 & 2031
1416
- if($postTypePost || $customPostType)
1417
- {
1418
- if(isset($GLOBALS['WPPH_POST_CATEGORIES_UPDATED']) || isset($GLOBALS['WPPH_POST_URL_UPDATED'])
1419
- || isset($GLOBALS['WPPH_POST_AUTHOR_UPDATED']) || isset($GLOBALS['WPPH_POST_PROTECTED_TRANSITION'])
1420
- || isset($GLOBALS['WPPH_POST_DATE_CHANGED']) || isset($GLOBALS['WPPH_PREVENT_BUBBLE'])){}
1421
- // Modified the published blog post %post_title%. Blog post URL is %post_URL%
1422
- else {
1423
- if(! $authorChanged){
1424
- WPPHPost::publishedPostUpdated($userID, $postTitle, $postUrl, ($postTypePost) ? 2002 : 2031);
1425
- }
1426
- }
1427
- }
1428
- else
1429
- {
1430
- if(isset($GLOBALS['WPPH_PAGE_URL_UPDATED']) || isset($GLOBALS['WPPH_PAGE_AUTHOR_UPDATED'])
1431
- || isset($GLOBALS['WPPH_PAGE_PROTECTED_TRANSITION']) || isset($GLOBALS['WPPH_POST_DATE_CHANGED'])
1432
- || isset($GLOBALS['WPPH_PREVENT_BUBBLE'])){}
1433
- // Modified the published page %page_title%. Page URL is %URL%
1434
- else {
1435
- if(! $authorChanged){
1436
- WPPHPost::publishedPostUpdated($userID, $postTitle, $postUrl, 2006);
1437
- }
1438
- }
1439
- }
1440
- // no need to process further
1441
- return;
1442
- }
1443
-
1444
- // if post name changed - we probably have a URL change here
1445
- // 2003 & 2007 & 2032
1446
- if($postModified){
1447
- if( isset($GLOBALS['WPPH_PAGE_AUTHOR_UPDATED']) || isset($GLOBALS['WPPH_PAGE_AUTHOR_UPDATED'])
1448
- || isset($GLOBALS['WPPH_POST_CATEGORIES_UPDATED'])|| isset($GLOBALS['WPPH_POST_DATE_CHANGED'])){}
1449
- else {
1450
- $event = 0;
1451
- if($customPostType) { $event = 2032;}
1452
- elseif($postTypePost) { $event = 2003; }
1453
- elseif($postTypePage) { $event = 2007; }
1454
- if(! empty($event)){
1455
- WPPHPost::draftPostUpdated($userID, $postID, $postTitle, $event);
1456
- }
1457
- }
1458
- }
1459
- }
1460
-
1461
- static function watchTrash($postID)
1462
- {
1463
- wpphLog(__METHOD__.'() triggered by hook.');
1464
- // get info for the currently logged in user
1465
- $current_user = wp_get_current_user();
1466
- global $wpdb;
1467
- $postInfo = $wpdb->get_row("SELECT post_title, post_type FROM ".$wpdb->posts." WHERE ID = ".$postID);
1468
- $postTitle = $postInfo->post_title;
1469
- $postType = $postInfo->post_type;
1470
- $customPostType = false;
1471
- $postTypePost = (($postType == 'post') ? true : false);
1472
- $postTypePage = (($postType == 'page') ? true : false);
1473
- if(!$postTypePost && !$postTypePage){
1474
- if(WPPHPost::validatePostType($postType)){
1475
- $customPostType = true;
1476
- }
1477
- }
1478
- $event = 0;
1479
- if($customPostType) { $event = 2033;}
1480
- elseif($postTypePost) { $event = 2008; }
1481
- elseif($postTypePage) { $event = 2009; }
1482
- if(! empty($event)){
1483
- if($event == 2033){
1484
- self::_addLogEvent($event, $current_user->ID, WPPHUtil::getIP(), array($postTitle, ucfirst($postType), $postID));
1485
- }
1486
- else { self::_addLogEvent($event, $current_user->ID, WPPHUtil::getIP(), array($postTitle,$postID)); }
1487
-
1488
- wpphLog('Post/Page deleted.', array('title'=>$postTitle, 'id'=>$postID));
1489
- }
1490
- }
1491
-
1492
- // 2010
1493
- static function watchFileUploaded($attachmentID)
1494
- {
1495
- global $wpdb;
1496
- // get info for the currently logged in user
1497
- $current_user = wp_get_current_user();
1498
- $rowData = $wpdb->get_row("SELECT guid FROM ".$wpdb->posts." WHERE ID = ".$attachmentID);
1499
- $fileName = basename($rowData->guid);
1500
- $dirName = dirname($rowData->guid);
1501
- // Uploaded the file %file name$ in %file location%
1502
- self::_addLogEvent(2010, $current_user->ID, WPPHUtil::getIP(), array($fileName, $dirName));
1503
- wpphLog('File uploaded.', array('title'=>$fileName, 'url'=>$dirName));
1504
- $GLOBALS['WPPH_PLUGIN_FILE_UPLOADED_IGNORE_DELETE'] = true;
1505
- }
1506
- // 2011
1507
- static function watchFileUploadedDeleted($attachmentID)
1508
- {
1509
- if(isset($GLOBALS['WPPH_PLUGIN_FILE_UPLOADED_IGNORE_DELETE'])){
1510
- // return, because if this variable is set this means this action is
1511
- // triggered inside WP after uploading a plugin and there's no reason to log this event.
1512
- return;
1513
- }
1514
- global $wpdb;
1515
- // get info for the currently logged in user
1516
- $current_user = wp_get_current_user();
1517
- $rowData = $wpdb->get_row("SELECT post_title, guid FROM ".$wpdb->posts." WHERE ID = ".$attachmentID);
1518
- // Deleted file %file name$ from %file_location%
1519
- self::_addLogEvent(2011, $current_user->ID, WPPHUtil::getIP(), array($rowData->post_title,dirname($rowData->guid)));
1520
- wpphLog('File deleted.', array('title'=>$rowData->post_title, 'url'=>dirname($rowData->guid)));
1521
- }
1522
-
1523
- // 2012, 2013, 2034
1524
- static function watchFileDeletion($postID)
1525
- {
1526
- global $wpdb;
1527
- $userID = wp_get_current_user()->ID;
1528
- $postInfo = $wpdb->get_row("SELECT post_title, post_type FROM ".$wpdb->posts." WHERE ID = ".$postID);
1529
- $postTitle = $postInfo->post_title;
1530
- $postType = $postInfo->post_type;
1531
- $customPostType = false;
1532
- $postTypePost = (($postType == 'post') ? true : false);
1533
- $postTypePage = (($postType == 'page') ? true : false);
1534
- if(!$postTypePost && !$postTypePage){
1535
- if(WPPHPost::validatePostType($postType)){
1536
- $customPostType = true;
1537
- }
1538
- }
1539
- $event = 0;
1540
- if($customPostType) { $event = 2034;}
1541
- elseif($postTypePost) { $event = 2012; }
1542
- elseif($postTypePage) { $event = 2013; }
1543
- if(! empty($event)){
1544
- if($event == 2034){
1545
- self::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst($postType)));
1546
- }
1547
- else { self::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle)); }
1548
- wpphLog('Post/Page trashed.', array('name'=>$postTitle));
1549
- }
1550
- }
1551
-
1552
- // 2014, 2015, 2035
1553
- static function watchTrashUndo($postID)
1554
- {
1555
- global $wpdb;
1556
- $userID = wp_get_current_user()->ID;
1557
- $postInfo = $wpdb->get_row("SELECT post_title, post_type FROM ".$wpdb->posts." WHERE ID = ".$postID);
1558
- $postTitle = $postInfo->post_title;
1559
- $postType = $postInfo->post_type;
1560
- $customPostType = false;
1561
- $postTypePost = (($postType == 'post') ? true : false);
1562
- $postTypePage = (($postType == 'page') ? true : false);
1563
- if(!$postTypePost && !$postTypePage){
1564
- if(WPPHPost::validatePostType($postType)){
1565
- $customPostType = true;
1566
- }
1567
- }
1568
- $event = 0;
1569
- if($customPostType) { $event = 2035;}
1570
- elseif($postTypePost) { $event = 2014; }
1571
- elseif($postTypePage) { $event = 2015; }
1572
- if(! empty($event)){
1573
- if($event == 2035){
1574
- self::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst($postType)));
1575
- }
1576
- else { self::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle)); }
1577
- wpphLog('Post/Page restored from trash.', array('name'=>$postTitle));
1578
- }
1579
- }
1580
-
1581
- static function watchWidgetActivity()
1582
- {
1583
- if ('POST' != strtoupper($_SERVER['REQUEST_METHOD']))
1584
- {
1585
- return;
1586
- }
1587
- if(!isset($_POST['widget-id']) || empty($_POST['widget-id'])){
1588
- return;
1589
- }
1590
-
1591
- $postData = $_POST;
1592
-
1593
- wpphLog(__METHOD__.'() triggered by hook.');
1594
- //wpphLog('POST DATA', $postData);
1595
-
1596
- global $wp_registered_sidebars;
1597
- $canCheckSidebar = (empty($wp_registered_sidebars) ? false : true);
1598
- $userID = wp_get_current_user()->ID;
1599
-
1600
- // if widget added
1601
- if(isset($postData['add_new']) && $postData['add_new'] == 'multi')
1602
- {
1603
- $widgetType = $postData['id_base'];
1604
- $sidebar = $postData['sidebar'];
1605
- if($canCheckSidebar && preg_match("/^sidebar-/",$sidebar)){
1606
- $sidebar = $wp_registered_sidebars[$sidebar]['name'];
1607
- }
1608
- self::_addLogEvent(2042, $userID, WPPHUtil::getIP(), array($widgetType, $sidebar));
1609
- wpphLog('User added a widget.', array('type'=>$widgetType, 'sidebar'=>$sidebar));
1610
- }
1611
- // if widget deleted
1612
- elseif(isset($postData['delete_widget']) && intval($postData['delete_widget']) == 1)
1613
- {
1614
- $widgetType = $postData['id_base'];
1615
- $sidebar = $postData['sidebar'];
1616
- if($canCheckSidebar && preg_match("/^sidebar-/",$sidebar)){
1617
- $sidebar = $wp_registered_sidebars[$sidebar]['name'];
1618
- }
1619
- self::_addLogEvent(2044, $userID, WPPHUtil::getIP(), array($widgetType, $sidebar));
1620
- wpphLog('User deleted a widget.', array('type'=>$widgetType, 'sidebar'=>$sidebar));
1621
- }
1622
- // if widget modified
1623
- elseif(isset($postData['id_base']) && !empty($postData['id_base']))
1624
- {
1625
- wpphLog('CHECKING IF WIDGET MODIFIED....');
1626
- // get info from $_POST
1627
- $wId = 0;
1628
- if(! empty($postData['multi_number'])){
1629
- $wId = intval($postData['multi_number']);
1630
- }
1631
- else {
1632
- if(! empty($postData['widget_number'])){
1633
- $wId = intval($postData['widget_number']);
1634
- }
1635
- }
1636
- if(empty($wId)){
1637
- wpphLog('EMPTY $wId');
1638
- return;
1639
- }
1640
- $wName = $postData['id_base'];
1641
- $sidebar = $postData['sidebar'];
1642
- $wData = isset($postData["widget-".$wName][$wId]) ? $postData["widget-".$wName][$wId] : null;
1643
-
1644
- if(empty($wData)){
1645
- wpphLog('EMPTY $wData');
1646
- return;
1647
- }
1648
- // get info from db
1649
- $wdbData = get_option("widget_".$wName);
1650
- if(empty($wdbData[$wId])){
1651
- wpphLog('EMPTY $wbData[$wId]');
1652
- return;
1653
- }
1654
- // transform 'on' -> 1
1655
- foreach($wData as $k=>&$v){
1656
- if($v == 'on'){ $v = 1; }
1657
- }
1658
- // compare - checks for any changes inside widgets
1659
- $diff = array_diff_assoc($wData, $wdbData[$wId]);
1660
- $count = count($diff);
1661
- if($count > 0){
1662
- if($canCheckSidebar && preg_match("/^sidebar-/",$sidebar)){
1663
- $sidebar = $wp_registered_sidebars[$sidebar]['name'];
1664
- }
1665
- //wpphLog('DIFF EXISTS.', array('wdata'=>$wData, 'wdbdata'=>$wdbData[$wId], 'diff'=>$diff));
1666
- self::_addLogEvent(2043, $userID, WPPHUtil::getIP(), array($wName, $sidebar));
1667
- wpphLog('User modified a widget.', array('type'=>$wName, 'sidebar'=>$sidebar));
1668
- }
1669
- else {wpphLog('No change.');}
1670
- }
1671
- }
1672
- //@ 2045
1673
- static function watchWidgetMove()
1674
- {
1675
- if(isset($_POST) && !empty($_POST['sidebars']))
1676
- {
1677
- wpphLog('Checking for moved widgets');
1678
- $crtSidebars = $_POST['sidebars'];
1679
- $sidebars = array();
1680
- //-- WP
1681
- foreach ( $crtSidebars as $key => $val ) {
1682
- $sb = array();
1683
- if ( !empty($val) ) {
1684
- $val = explode(',', $val);
1685
- foreach ( $val as $k => $v ) {
1686
- if ( strpos($v, 'widget-') === false ){ continue; }
1687
- $sb[$k] = substr($v, strpos($v, '_') + 1);
1688
- }
1689
- }
1690
- $sidebars[$key] = $sb;
1691
- }
1692
- //-- WP
1693
- $crtSidebars = $sidebars;
1694
- $dbSidebars = get_option('sidebars_widgets');
1695
- $wName = $fromSidebar = $toSidebar = '';
1696
- foreach($crtSidebars as $sidebarName => $values)
1697
- {
1698
- if(is_array($values) && ! empty($values))
1699
- {
1700
- if(isset($dbSidebars[$sidebarName]))
1701
- {
1702
- foreach($values as $i => $widgetName)
1703
- {
1704
- if(! in_array($widgetName, $dbSidebars[$sidebarName])){
1705
- $toSidebar = $sidebarName;
1706
- $wName = $widgetName;
1707
- foreach($dbSidebars as $name => $v){
1708
- if(is_array($v) && !empty($v)){
1709
- if(in_array($widgetName, $v)){
1710
- $fromSidebar = $name;
1711
- break;
1712
- }
1713
- }
1714
- }
1715
- }
1716
- }
1717
- }
1718
- }
1719
- }
1720
- if(empty($wName) || empty($fromSidebar) || empty($toSidebar)){
1721
- wpphLog('No change.');
1722
- return;
1723
- }
1724
-
1725
- $userID = wp_get_current_user()->ID;
1726
- $ip = WPPHUtil::getIP();
1727
-
1728
- if(preg_match("/^sidebar-/", $fromSidebar) || preg_match("/^sidebar-/",$toSidebar)){
1729
- // This option will hold the data needed to trigger the event 2045
1730
- // as at this moment the $wp_registered_sidebars variable is not yet populated
1731
- // so we cannot retrieve the name for sidebar-1 || sidebar-2
1732
- // we will then check for this variable in the triggerWidgetMoveEvent() function
1733
- $GLOBALS['WPPH_WIDGET_MOVE'] = array('widget'=>$wName, 'from'=>$fromSidebar, 'to'=>$toSidebar, 'user'=>$userID, 'ip', 'ip'=>$ip);
1734
- wpphLog('Widget moved. Data saved to variable: WPPH_WIDGET_MOVE');
1735
- return;
1736
- }
1737
- self::_addLogEvent(2045, $userID, $ip, array($wName, $fromSidebar, $toSidebar));
1738
- }
1739
- }
1740
-
1741
- static function triggerWidgetMoveEvent()
1742
- {
1743
- wpphLog(__METHOD__.'() triggered.');
1744
- $data = (isset($GLOBALS['WPPH_WIDGET_MOVE']) ? $GLOBALS['WPPH_WIDGET_MOVE'] : null);
1745
- if(empty($data)){
1746
- wpphLog("Global var: WPPH_WIDGET_MOVE not available yet. Nothing to do here.");
1747
- return;
1748
- }
1749
- $from = $data['from'];
1750
- $to = $data['to'];
1751
- global $wp_registered_sidebars;
1752
- if(preg_match("/^sidebar-/", $from)){ $from = (isset($wp_registered_sidebars[$from]) ? $wp_registered_sidebars[$from]['name'] : $from); }
1753
- if(preg_match("/^sidebar-/", $to)){ $to = (isset($wp_registered_sidebars[$to]) ? $wp_registered_sidebars[$to]['name'] : $to); }
1754
- self::_addLogEvent(2045, $data['user'], $data['ip'], array($data['widget'], $from, $to));
1755
- }
1756
-
1757
-
1758
- // 3000 - Theme activated
1759
- static function watchThemeChange($themeName)
1760
- {
1761
- // get info for the currently logged in user
1762
- $current_user = wp_get_current_user();
1763
- // // Activated the theme %themeName%
1764
- self::_addLogEvent(3000, $current_user->ID, WPPHUtil::getIP(), array($themeName));
1765
- wpphLog('Theme activated.', array('name'=>$themeName));
1766
- }
1767
-
1768
- // 2023 - category created
1769
- static function watchCategoryAdd(array $postData)
1770
- {
1771
- wpphLog(__METHOD__.'() triggered by hook.');
1772
-
1773
- if(!empty($_POST['screen']) && !empty($_POST['tag-name']) &&
1774
- $_POST['screen'] == 'edit-category' &&
1775
- $_POST['taxonomy'] == 'category' &&
1776
- $_POST['action'] == 'add-tag')
1777
- {
1778
- $categoryName = sanitize_text_field($_POST['tag-name']);
1779
-
1780
- // get info for the currently logged in user
1781
- $current_user = wp_get_current_user();
1782
- // Created a new category called %categoryName%
1783
- self::_addLogEvent(2023, $current_user->ID, WPPHUtil::getIP(), array($categoryName));
1784
- wpphLog('Category added.', array('name'=>$categoryName));
1785
- }
1786
- // adding the new category when writing a blog post
1787
- elseif(! empty($_POST['newcategory']) && $_POST['action'] == 'add-category')
1788
- {
1789
- $categoryName = sanitize_text_field($_POST['newcategory']);
1790
-
1791
- // get info for the currently logged in user
1792
- $current_user = wp_get_current_user();
1793
- // Created a new category called %categoryName%
1794
- self::_addLogEvent(2023, $current_user->ID, WPPHUtil::getIP(), array($categoryName));
1795
- wpphLog('Category added.', array('name'=>$categoryName));
1796
- }
1797
- }
1798
-
1799
- // 2024 - category deleted
1800
- static function watchCategoryDelete(array $postData)
1801
- {
1802
- wpphLog(__METHOD__.'() triggered by hook.');
1803
-
1804
- if(empty($postData)){ return; }
1805
-
1806
- // get info for the currently logged in user
1807
- $current_user = wp_get_current_user();
1808
- $userID = $current_user->ID;
1809
- $userIP = WPPHUtil::getIP();
1810
-
1811
- //@internal
1812
- function __alertDeletedCategory($userID, $userIP, $categoryID){
1813
- $category = get_category($categoryID);
1814
- $categoryName = $category->cat_name;
1815
- WPPHEvent::_addLogEvent(2024, $userID, $userIP, array($categoryName));
1816
- wpphLog('Category deleted.', array('name'=>$categoryName));
1817
- }
1818
-
1819
- $action = '';
1820
- if(! empty($postData['action'])){ $action = $postData['action'];}
1821
- elseif(! empty($postData['action2'])){ $action = $postData['action2'];}
1822
- if(empty($action)) {
1823
- return;
1824
- }
1825
-
1826
- // delete one
1827
- if($action == 'delete-tag' && $postData['taxonomy'] == 'category' && !empty($postData['tag_ID'])){
1828
- __alertDeletedCategory($userID, $userIP, $postData['tag_ID']);
1829
- }
1830
-
1831
- // bulk delete
1832
- elseif($action == 'delete' && $postData['taxonomy'] == 'category' && !empty($postData['delete_tags'])){
1833
- foreach($postData['delete_tags'] as $categoryID){
1834
- __alertDeletedCategory($userID, $userIP, $categoryID);
1835
- }
1836
- }
1837
- }
1838
-
1839
- // 2025, 2026
1840
- static function watchPostVisibilityChange($oldStatus, $newStatus, $userID, $postTitle, $post, $event)
1841
- {
1842
- $args = func_get_args();
1843
- wpphLog(__METHOD__.'() triggered.', array('params'=>$args));
1844
-
1845
- global $wpdb;
1846
-
1847
- $crtPostPassword = $wpdb->get_var("SELECT post_password FROM ".$wpdb->posts." WHERE ID = ".$post->ID);
1848
- $oldPostPassword = (isset($GLOBALS['WPPH_OLD_POST_PASSWORD']) ? $GLOBALS['WPPH_OLD_POST_PASSWORD'] : null);
1849
-
1850
- $from = $to = '';
1851
-
1852
- // public -> pwd protected
1853
- // pwd protected -> public
1854
- if($oldStatus == 'publish' && $newStatus == 'publish')
1855
- {
1856
- if(! WPPH::isMultisite()){
1857
- // if post is already pwd protected and there is no change, it will still be issued an event: public to pwd protected
1858
- if(isset($GLOBALS['WPPH_POST_PWD_PROTECTED']) && $GLOBALS['WPPH_POST_PWD_PROTECTED']){
1859
- $GLOBALS['WPPH_PREVENT_BUBBLE'] = true;
1860
- wpphLog(__METHOD__.'() No change.');
1861
- return;
1862
- }
1863
- }
1864
-
1865
- // pwd protected -> public
1866
- if(empty($crtPostPassword) && !empty($oldPostPassword)){
1867
- $from = __('Password Protected');
1868
- $to = __('Public');
1869
- }
1870
- // public -> pwd protected
1871
- else {
1872
- if(! empty($crtPostPassword)){
1873
- $from = __('Public');
1874
- $to = __('Password Protected');
1875
- }
1876
- }
1877
- }
1878
- // public -> private
1879
- // pwd protected -> private
1880
- elseif($oldStatus == 'publish' && $newStatus == 'private')
1881
- {
1882
- // public -> private
1883
- if(empty($crtPostPassword) && empty($oldPostPassword)){
1884
- $from = __('Public');
1885
- $to = __('Private');
1886
- }
1887
- // pwd protected -> private
1888
- else {
1889
- if(!empty($oldPostPassword)){
1890
- $from = __('Password Protected');
1891
- $to = __('Private');
1892
- }
1893
- }
1894
- }
1895
- // private -> public
1896
- // private -> pwd protected
1897
- elseif($oldStatus == 'private' && $newStatus == 'publish')
1898
- {
1899
- // private -> public
1900
- if(empty($oldPostPassword) && empty($crtPostPassword)){
1901
- $from = __('Private');
1902
- $to = __('Public');
1903
- }
1904
- // private -> pwd protected
1905
- else {
1906
- if(empty($oldPostPassword) && !empty($crtPostPassword)){
1907
- $from = __('Private');
1908
- $to = __('Password Protected');
1909
- }
1910
- }
1911
- }
1912
- wpphLog("Changing post visibility:",array('from'=>$from, 'to'=>$to));
1913
- if(empty($from) || empty($to)){
1914
- return;
1915
- }
1916
-
1917
- $GLOBALS['WPPH_PREVENT_BUBBLE'] = true;
1918
- WPPHPost::postVisibilityChanged($userID, $postTitle, $from, $to, $event);
1919
- }
1920
-
1921
- // 2027 & 2028
1922
- static function watchPostDateChange($userID, $postTitle, $postCurrentDate, $event)
1923
- {
1924
- wpphLog(__METHOD__.'() triggered.');
1925
-
1926
- if($GLOBALS['WPPH_POST_IS_NEW']){
1927
- wpphLog('Nothing to do. The post is brand new.');
1928
- return;
1929
- }
1930
- if(empty($GLOBALS['WPPH_POST_OLD_DATE'])){
1931
- wpphLog('Empty global WPPH_POST_OLD_DATE, nothing to do.');
1932
- return;
1933
- }
1934
-
1935
- $t1 = strtotime($GLOBALS['WPPH_POST_OLD_DATE']);
1936
- $t2 = strtotime($postCurrentDate);
1937
-
1938
- if($t1 == $t2){
1939
- wpphLog('No change.');
1940
- return;
1941
- }
1942
- if(empty($t1) || empty($postCurrentDate)){
1943
- wpphLog('Empty $t1 or $postCurrentDate. Nothing to do.');
1944
- return;
1945
- }
1946
-
1947
- $format = get_option('date_format');
1948
- $from = date($format, $t1);
1949
- $to = date($format, $t2);
1950
- if($from == $to){
1951
- wpphLog('No date change.');
1952
- return;
1953
- }
1954
- wpphLog('POST DATE CHANGED',array(
1955
- 'from' => $from . '('.$t1.')',
1956
- 'to' => $to . '('.$t2.')'
1957
- ));
1958
- WPPHPost::postDateChanged($userID, $postTitle, $from, $to, $event);
1959
- }
1960
-
1961
- static function watchPostCategoriesChange($post, $wpdb, $postTitle, $event)
1962
- {
1963
- wpphLog(__METHOD__.'() triggered.');
1964
-
1965
- if(! wpph_isEventEnabled($event)){
1966
- wpphLog('Event '.$event.' is not enabled. Ignoring request.');
1967
- return true;
1968
- }
1969
-
1970
- if(isset($GLOBALS['WPPH_POST_OLD_CATEGORIES']))
1971
- {
1972
- $originalCats = $GLOBALS['WPPH_POST_OLD_CATEGORIES'];
1973
- $categories_1 = array();
1974
- foreach($originalCats as $catID){
1975
- $cat = get_category($catID);
1976
- array_push($categories_1, $cat->name);
1977
- }
1978
- $categories_2 = array();
1979
- $newCats = $post->post_category;
1980
- if(empty($newCats)){
1981
- wpphLog('No categories found for this post.');
1982
- return true;
1983
- }
1984
- wpphLog('$newCats', $newCats);
1985
- foreach($newCats as $catID){
1986
- if(empty($catID)){
1987
- wpphLog('Category is empty: '.$catID);
1988
- continue;
1989
- }
1990
- $cat = get_category($catID);
1991
- array_push($categories_2, $cat->name);
1992
- }
1993
-
1994
- sort($categories_1);
1995
- sort($categories_2);
1996
-
1997
- // categories updated
1998
- if($categories_1 <> $categories_2)
1999
- {
2000
- if(empty($categories_1)){
2001
- // get the name of the default category
2002
- $optID = get_option('default_category');
2003
- $query = $wpdb->prepare("SELECT wpt.name FROM ".$wpdb->terms." AS wpt
2004
- INNER JOIN ".$wpdb->options." AS wpo
2005
- WHERE wpo.option_id = %d
2006
- AND wpt.term_id = %d;", $optID, $optID);
2007
- $defaultCategoryName = $wpdb->get_var($query);
2008
-
2009
- // if categories-2 contains only the name of the default category...
2010
- if(count($categories_2) == 1){
2011
- if(strcasecmp($categories_2[0], $defaultCategoryName) == 0){
2012
- // nothing to do here...
2013
- $GLOBALS['WPPH_POST_CATEGORIES_UPDATED'] = true;
2014
- }
2015
- }
2016
- else {
2017
- $c1 = implode(', ', $categories_1);
2018
- $c2 = implode(', ', $categories_2);
2019
- WPPHPost::postCategoriesUpdated(wp_get_current_user()->ID, $postTitle, $c1, $c2, $event);
2020
- $GLOBALS['WPPH_POST_CATEGORIES_UPDATED'] = true;
2021
- }
2022
- }
2023
- else {
2024
- $c1 = implode(', ', $categories_1);
2025
- $c2 = implode(', ', $categories_2);
2026
- WPPHPost::postCategoriesUpdated(wp_get_current_user()->ID, $postTitle, $c1, $c2, $event);
2027
- $GLOBALS['WPPH_POST_CATEGORIES_UPDATED'] = true;
2028
- }
2029
- }
2030
- }
2031
- }
2032
-
2033
- // 2017 & 2018 - Post/page modified
2034
- // convenience method to trigger a post/page modified event
2035
- static function watchPostChanged($wpdb, $postID)
2036
- {
2037
- wpphLog(__METHOD__.'() triggered.');
2038
-
2039
- if(isset($GLOBALS['WPPH_POST_OLD_NAME'])){
2040
- // get the current post name and compare
2041
- $result = $wpdb->get_row("SELECT post_title, post_name, post_password, post_date FROM ".$wpdb->posts." WHERE ID = $postID");
2042
- if(empty($result)){ return false; }
2043
- $postName = (empty($result->post_name) ? $result->post_title : $result->post_name);
2044
- return($GLOBALS['WPPH_POST_OLD_NAME'] != $postName);
2045
- }
2046
- return false;
2047
- }
2048
-
2049
-
2050
- // 7000 new site added to network
2051
- static function watchBlogAdded($blog_id, $user_id)
2052
- {
2053
- if(empty($blog_id)) { return; }
2054
- $blogName = WPPHNetwork::getBlogName($blog_id);
2055
- if(empty($blogName)) { return; }
2056
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2057
- WPPHEvent::_addLogEvent(7000, wp_get_current_user()->ID, WPPHUtil::getIP(), array($blogName), null, $currentBlogID);
2058
- wpphLog(__METHOD__.'() triggered by hook. Blog added: '.$blogName.'; user ID = '.$user_id);
2059
- }
2060
-
2061
- // 7001 - blog archived
2062
- static function watchBlogArchived($blog_id)
2063
- {
2064
- if(empty($blog_id)) { return; }
2065
- $blogName = WPPHNetwork::getBlogName($blog_id);
2066
- if(empty($blogName)) { return; }
2067
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2068
- WPPHEvent::_addLogEvent(7001, wp_get_current_user()->ID, WPPHUtil::getIP(), array($blogName), null, $currentBlogID);
2069
- wpphLog(__METHOD__.'() triggered by hook. Blog archived: '.$blogName);
2070
- }
2071
-
2072
- // 7002 - blog unarchived
2073
- static function watchBlogUnarchived($blog_id)
2074
- {
2075
- if(empty($blog_id)) { return; }
2076
- $blogName = WPPHNetwork::getBlogName($blog_id);
2077
- if(empty($blogName)) { return; }
2078
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2079
- WPPHEvent::_addLogEvent(7002, wp_get_current_user()->ID, WPPHUtil::getIP(), array($blogName), null, $currentBlogID);
2080
- wpphLog(__METHOD__.'() triggered by hook. Blog unarchived: '.$blogName);
2081
- }
2082
-
2083
- // 7003 - blog activated
2084
- static function watchBlogActivated($blog_id)
2085
- {
2086
- if(empty($blog_id)) { return; }
2087
- $blogName = WPPHNetwork::getBlogName($blog_id);
2088
- if(empty($blogName)) { return; }
2089
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2090
- WPPHEvent::_addLogEvent(7003, wp_get_current_user()->ID, WPPHUtil::getIP(), array($blogName), null, $currentBlogID);
2091
- wpphLog(__METHOD__.'() triggered by hook. Blog activated: '.$blogName);
2092
- }
2093
-
2094
- // 7004 - blog deactivated
2095
- static function watchBlogDeactivated($blog_id)
2096
- {
2097
- if(empty($blog_id)) { return; }
2098
- $blogName = WPPHNetwork::getBlogName($blog_id);
2099
- if(empty($blogName)) { return; }
2100
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2101
- WPPHEvent::_addLogEvent(7004, wp_get_current_user()->ID, WPPHUtil::getIP(), array($blogName), null, $currentBlogID);
2102
- wpphLog(__METHOD__.'() triggered by hook. Blog deactivated: '.$blogName);
2103
- }
2104
-
2105
- // 7005 - blog deleted
2106
- static function watchBlogDeleted($blog_id)
2107
- {
2108
- if(empty($blog_id)) { return; }
2109
- $blogName = WPPHNetwork::getBlogName($blog_id);
2110
- if(empty($blogName)) { return; }
2111
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2112
- WPPHEvent::_addLogEvent(7005, wp_get_current_user()->ID, WPPHUtil::getIP(), array($blogName), null, $currentBlogID);
2113
- wpphLog(__METHOD__.'() triggered by hook. Blog deleted: ', array('id'=>$blog_id, 'name'=>$blogName));
2114
- }
2115
-
2116
- // 4008 && 4009
2117
- static function watchUserAdminPrivUpdated($userID)
2118
- {
2119
- wpphLog(__METHOD__.'() triggered by hook.');
2120
- wpphLog('POST DATA', $_POST);
2121
-
2122
- // 4008
2123
- if(isset($_POST['super_admin']) && !empty($_POST['super_admin']))
2124
- {
2125
- $bid = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2126
- $userName = trim($_POST['display_name']);
2127
- WPPHEvent::_addLogEvent(4008, wp_get_current_user()->ID, WPPHUtil::getIP(), array($userName), null, $bid);
2128
- }
2129
- // 4009
2130
- else {
2131
- $bid = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2132
- $superAdmins = get_super_admins();
2133
- $u = new WP_User($userID);
2134
- $userName = $u->get('user_login');
2135
- if(! empty($superAdmins)){
2136
- foreach($superAdmins as $admin){
2137
- if($admin == $userName){
2138
- WPPHEvent::_addLogEvent(4009, wp_get_current_user()->ID, WPPHUtil::getIP(), array($userName), null, $bid);
2139
- return;
2140
- }
2141
- }
2142
- }
2143
- }
2144
- }
2145
-
2146
- // 4010
2147
- static function watchUserAddedToBlog($user_id, $role, $blog_id)
2148
- {
2149
- wpphLog(__METHOD__.'() triggered with params: ', array(
2150
- '$user_id' => $user_id,
2151
- '$role' => $role,
2152
- '$blog_id' => $blog_id
2153
- ));
2154
-
2155
- // current blog id
2156
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2157
-
2158
- // get current user info
2159
- $userInfo = WPPHDB::getUserInfo($user_id);
2160
- $userName = $userInfo['userName'];
2161
- $siteName = WPPHNetwork::getBlogName($blog_id);
2162
-
2163
- WPPHEvent::_addLogEvent(4010, wp_get_current_user()->ID, WPPHUtil::getIP(), array($userName, $role, $siteName), null, $currentBlogID);
2164
- wpphLog('User added to site.');
2165
- }
2166
-
2167
- // 4011
2168
- static function watchUserRemovedFromBlog($user_id)
2169
- {
2170
- $blog_id = (isset($_REQUEST['id']) ? $_REQUEST['id'] : 0);
2171
- wpphLog(__METHOD__.'() triggered with params: ', array(
2172
- '$user_id' => $user_id,
2173
- '$blog_id' => $blog_id
2174
- ));
2175
-
2176
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2177
-
2178
- // get current user info
2179
- $userInfo = WPPHDB::getUserInfo($user_id);
2180
- $userName = $userInfo['userName'];
2181
- $userRole = $userInfo['userRole'];
2182
- $siteName = WPPHNetwork::getBlogName($blog_id);
2183
-
2184
- if(empty($currentBlogID) || empty($userName) || empty($userRole) || empty($siteName)){
2185
- wpphLog('Cannot trigger the event 4011. One or more required variables are empty.', array(
2186
- '$currentBlogID' => $currentBlogID,
2187
- '$userName' => $userName,
2188
- '$userRole' => $userRole,
2189
- '$siteName' => $siteName,
2190
- ));
2191
- return;
2192
- }
2193
-
2194
- WPPHEvent::_addLogEvent(4011, wp_get_current_user()->ID, WPPHUtil::getIP(), array($userName, $userRole, $siteName), null, $currentBlogID);
2195
- wpphLog('User removed from site.');
2196
- }
2197
-
2198
- // 4012
2199
- static function watchWpmuUserRegister($user_id)
2200
- {
2201
- wpphLog(__METHOD__.'() triggered with params: ', array('$user_id' => $user_id));
2202
-
2203
- $currentBlogID = WPPHNetwork::getGlobalOption(WPPH_MAIN_SITE_ID_OPTION_NAME, false, true);
2204
- $userInfo = WPPHDB::getUserInfo($user_id);
2205
- $userName = $userInfo['userName'];
2206
-
2207
- WPPHEvent::_addLogEvent(4012, wp_get_current_user()->ID, WPPHUtil::getIP(), array($userName), null, $currentBlogID);
2208
- wpphLog('Created new network user.');
2209
- }
2210
-
2211
-
2212
-
2213
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/WPPHLogger.php DELETED
@@ -1,40 +0,0 @@
1
- <?php
2
- // custom function
3
- function wpphLog($message, $data=null, $function=null,$line=null){ WPPHLogger::write($message,$data,$function,$line); }
4
- /*
5
- * @internal
6
- * Debug class
7
- * DO NOT enable this on live website
8
- */
9
- class WPPHLogger
10
- {
11
- private static $_debugLoggingEnabled = false;
12
-
13
- private static $_fileName = 'debug.log';
14
-
15
- public static function enableDebugLogging(){ self::$_debugLoggingEnabled = true; }
16
- public static function enableErrorLogging(){ ini_set('error_log', WPPH_PLUGIN_DIR.'error.log'); }
17
-
18
- public static function write($message, $data=null, $function=null, $line=null)
19
- {
20
- if(!empty($_POST)){
21
- if(isset($_POST['action']) && $_POST['action'] == 'heartbeat'){ return;}
22
- }
23
-
24
- if(!self::$_debugLoggingEnabled) { return; }
25
-
26
- $m = '['.@date("D, M d, Y @H:i:s").'] Debug: '.$message;
27
- if(!empty($function)){
28
- $m .= PHP_EOL.'Function: '.$function;
29
- if(! empty($line)){
30
- $m .= PHP_EOL.'Line: '.$line.PHP_EOL;
31
- }
32
- }
33
- if(! empty($data)) {
34
- $m .= ' Data: '.var_export($data, true);
35
- }
36
- $m .= PHP_EOL;
37
-
38
- @file_put_contents(WPPH_PLUGIN_DIR.self::$_fileName,$m,FILE_APPEND);
39
- }
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/WPPHNetwork.php DELETED
@@ -1,232 +0,0 @@
1
- <?php
2
- /**
3
- * Class wpphNetwork
4
- */
5
- class WPPHNetwork
6
- {
7
- // runs per site
8
- static function _activate($blogId=1)
9
- {
10
- wpphLog("Activating plugin for blog: $blogId");
11
-
12
- // Network ID
13
- update_option(WPPH_NETWORK_ID_OPTION_NAME, (defined('SITE_ID_CURRENT_SITE') ? SITE_ID_CURRENT_SITE : 1));
14
- // Main Site ID
15
- update_option(WPPH_MAIN_SITE_ID_OPTION_NAME, (defined('BLOG_ID_CURRENT_SITE') ? BLOG_ID_CURRENT_SITE : 1));
16
-
17
- if(WPPH::isMultisite())
18
- {
19
- // if main site
20
- if(WPPHUtil::isMainSite()){
21
- WPPH::createPluginDefaultSettings();
22
- }
23
- // nothing to do here
24
- else {
25
- update_option('WPPH_PLUGIN_ACTIVATED',1);
26
- wpphLog("Plugin activated successfully for blog: $blogId");
27
- return true;
28
- }
29
- }
30
-
31
- $ok = WPPH::onPluginActivate($blogId);
32
- if($ok)
33
- {
34
- update_option('WPPH_PLUGIN_ACTIVATED',1);
35
- wpphLog("Plugin activated successfully for blog: $blogId");
36
- }
37
- else { wpphLog("Plugin could not be activated for blog: $blogId"); }
38
- }
39
- // runs per site
40
- static function _deactivate($blogId=1){
41
- wpphLog("Deactivating plugin for blog: $blogId");
42
- $ok = WPPH::onPluginDeactivate($blogId);
43
- if($ok){
44
- wpphLog("Plugin deactivated successfully for blog: $blogId");
45
- }
46
- else { wpphLog("Plugin could not be deactivated for blog: $blogId"); }
47
- }
48
- // runs per site
49
- static function _uninstall($blogId=1){
50
- wpphLog("Uninstalling plugin from blog: $blogId");
51
-
52
- //check first for the wpph_plugin_activated for each site and if active first deactivate and then uninstall
53
- $active = (int)get_option('WPPH_PLUGIN_ACTIVATED');
54
- if(!empty($active)){
55
- self::_deactivate($blogId);
56
- }
57
-
58
- global $wpdb;
59
-
60
- // check if any tables created for a particular blog inside the network exist
61
- $prefix = $wpdb->prefix;
62
-
63
- if(empty($blogId) || $blogId == 1){}
64
- else { $prefix .= $blogId.'_'; }
65
-
66
- $wpdb->query("DROP TABLE IF EXISTS ".$prefix.'_wordpress_eventlog');
67
- $wpdb->query("DROP TABLE IF EXISTS ".$prefix.'_wordpress_eventlog_details');
68
- $wpdb->query("DROP TABLE IF EXISTS ".$prefix.'wordpress_auditlog');
69
- $wpdb->query("DROP TABLE IF EXISTS ".$prefix.'wordpress_auditlog_events');
70
-
71
- delete_option('WPPH_CRON_TASK_NAME');
72
- delete_option('WPPH_NETWORK_INSTALL');
73
- delete_option('WPPH_PLUGIN_ACTIVATED');
74
- delete_option(WPPH_MAIN_SITE_ID_OPTION_NAME);
75
- delete_option(WPPH_NETWORK_ID_OPTION_NAME);
76
- delete_option(WPPH_PLUGIN_ALLOW_ACCESS_OPTION_NAME);
77
- delete_option(WPPH_PLUGIN_ALLOW_CHANGE_OPTION_NAME);
78
- delete_option(WPPH_PLUGIN_DB_UPDATED);
79
- delete_option(WPPH_PLUGIN_SETTING_NAME);
80
- delete_option(WPPH_PLUGIN_EVENTS_LIST_OPTION_NAME);
81
- delete_option(WPPH_PLUGIN_VERSION_OPTION_NAME);
82
- delete_option(WPPH_USERS_CAN_REGISTER_OPT_NAME);
83
- delete_option(WPPH_PLUGIN_ERROR_OPTION_NAME);
84
- wpphLog("Plugin successfully uninstalled.");
85
- }
86
-
87
- static function networkActivate(/*$networkwide=false*/)
88
- {
89
- global $wpdb;
90
- if (WPPH::isMultisite()) {
91
- $old_blog = $wpdb->blogid;
92
- $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
93
- foreach ($blogids as $blog_id) {
94
- switch_to_blog($blog_id);
95
- self::_activate($blog_id);
96
- }
97
- switch_to_blog($old_blog);
98
- update_option('WPPH_NETWORK_INSTALL',1);
99
- }else{
100
- update_option('WPPH_NETWORK_INSTALL',0);
101
- self::_activate($wpdb->blogid);
102
- }
103
- /*global $wpdb;
104
- if (WPPH::isMultisite()) {
105
- // check if it is a network activation - if so, run the activation function for each blog id
106
- if ($networkwide)
107
- {
108
- $old_blog = $wpdb->blogid;
109
- $blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
110
- foreach ($blogids as $blog_id) {
111
- switch_to_blog($blog_id);
112
- self::_activate($blog_id);
113
- }
114
- switch_to_blog($old_blog);
115
- update_option('WPPH_NETWORK_INSTALL',1);
116
- return;
117
- }
118
- }
119
- update_option('WPPH_NETWORK_INSTALL',0);
120
- self::_activate($wpdb->blogid);*/
121
- }
122
-
123
- static function networkDeactivate($networkwide=false){
124
- global $wpdb;
125
- if (WPPH::isMultisite()) {
126
- // check if it is a network activation - if so, run the activation function
127
- // for each blog id
128
- if ($networkwide) {
129
- $old_blog = $wpdb->blogid;
130
- // Get all blog ids
131
- $blogids = $wpdb->get_col("SELECT blog_id FROM {$wpdb->blogs}");
132
- foreach ($blogids as $blog_id) {
133
- switch_to_blog($blog_id);
134
- self::_deactivate($blog_id);
135
- }
136
- switch_to_blog($old_blog);
137
- return;
138
- }
139
- }
140
- self::_deactivate($wpdb->blogid);
141
- }
142
-
143
- static function networkUninstall(){
144
- global $wpdb;
145
- if (WPPH::isMultisite()) {
146
- $old_blog = $wpdb->blogid;
147
- // Get all blog ids
148
- $blogids = $wpdb->get_col("SELECT blog_id FROM {$wpdb->blogs}");
149
- foreach ($blogids as $blog_id) {
150
- switch_to_blog($blog_id);
151
- self::_uninstall($blog_id);
152
- }
153
- switch_to_blog($old_blog);
154
- return;
155
- }
156
- self::_uninstall();
157
- }
158
-
159
- /**
160
- * Retrieve the list of all blogs in the network
161
- * @return array( blogID => blogName )
162
- */
163
- static function getBlogsList()
164
- {
165
- global $wpdb;
166
- $out = array();
167
- $results = $wpdb->get_results("SELECT blog_id FROM $wpdb->blogs WHERE public = 1 ORDER BY blog_id", ARRAY_A);
168
- if(empty($results)){
169
- return $out;
170
- }
171
- foreach($results as $entry){
172
- $out[$entry['blog_id']] = self::getBlogName($entry['blog_id']);
173
- }
174
- return $out;
175
- }
176
-
177
- static function getBlogName($blogID)
178
- {
179
- return self::callOptionFunc('get', $blogID, 'blogname');
180
- }
181
-
182
- static function getGlobalBlogId(){
183
- return 1; // this is the norm
184
- }
185
-
186
- /**
187
- * Retrive the value of a global option
188
- * @param $optionName
189
- * @param bool $unserialize
190
- * @param bool $isConstant
191
- * @param null $default The default value to return if option not found
192
- * @return mixed string if $unserialize = false, otherwise array
193
- */
194
- static function getGlobalOption($optionName, $unserialize = false, $isConstant = false, $default = null)
195
- {
196
- return self::callOptionFunc('get', self::getGlobalBlogId(), $optionName, $default);
197
- }
198
-
199
- /**
200
- * Update a global option
201
- * @param string $optionName
202
- * @param mixed $optionValue
203
- * @param bool $serialize
204
- * @param bool $isConstant
205
- * @return bool
206
- */
207
- static function updateGlobalOption($optionName, $optionValue, $serialize = false, $isConstant = false)
208
- {
209
- return self::callOptionFunc('update', self::getGlobalBlogId(), $optionName, $optionValue);
210
- }
211
-
212
- static function addGlobalOption($optionName, $optionValue, $serialize = false, $isConstant = false)
213
- {
214
- return self::callOptionFunc('add', self::getGlobalBlogId(), $optionName, $optionValue);
215
- }
216
-
217
- /**
218
- * Gets, adds or updates a wordpress option.
219
- * @param string $action An action: get, add, update
220
- * @param integer $blogid Blog id, used on multisite only.
221
- * @param mixed Rest of the function arguments.
222
- * @return type
223
- */
224
- static function callOptionFunc($action, $blogid){
225
- $args = func_get_args();
226
- $action = array_shift($args);
227
- $musite = function_exists($action.'_blog_option');
228
- $fnname = $musite ? "{$action}_blog_option" : "{$action}_option";
229
- if(!$musite)$blogid = array_shift($args); // remove blogid when not multisite
230
- return call_user_func_array($fnname, $args);
231
- }
232
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/WPPHPost.php DELETED
@@ -1,191 +0,0 @@
1
- <?php
2
-
3
- class WPPHPost
4
- {
5
- public static $currentPostType = '';
6
-
7
- static function getPostTypes()
8
- {
9
- $args = array('public' => true,'_builtin' => false);
10
- $output = 'names'; // names or objects, note names is the default
11
- $operator = 'and'; // 'and' or 'or'
12
-
13
- $result = get_post_types( $args, $output, $operator );
14
- if(! isset($result['post'])){ $result['post'] = 'post'; }
15
- if(! isset($result['page'])){ $result['page'] = 'page'; }
16
- return $result;
17
- }
18
-
19
- static function validatePostType($postType)
20
- {
21
- if(empty($postType)){
22
- return false;
23
- }
24
- $types = self::getPostTypes();
25
- wpphLog('POST TYPES',$types);
26
- return (in_array($postType, $types) ? true : false);
27
- }
28
-
29
- // 2019 & 2020 & 2038
30
- static function managePostAuthorUpdateQuickEditForm($data, $postArray)
31
- {
32
- if($data['post_type'] == 'post'){
33
- if(self::postAuthorChanged($GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID'], $postArray['ID'], wp_get_current_user()->ID, $data['post_title'], 2019, true)){
34
- $GLOBALS['WPPH_POST_AUTHOR_UPDATED'] = true;
35
- }
36
- }
37
- elseif($data['post_type'] == 'page'){
38
- if(self::postAuthorChanged($GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID'], $postArray['ID'], wp_get_current_user()->ID, $data['post_title'], 2020, true)){
39
- $GLOBALS['WPPH_PAGE_AUTHOR_UPDATED'] = true;
40
- }
41
- }
42
- // custom post type
43
- else {
44
- self::$currentPostType = $data['post_type'];
45
- wpphLog('CURRENT POST TYPE: '.self::$currentPostType);
46
- if(self::postAuthorChanged($GLOBALS['WPPH_POST_AUTHOR_UPDATED_ID'], $postArray['ID'], wp_get_current_user()->ID, $data['post_title'], 2038, true)){
47
- $GLOBALS['WPPH_POST_AUTHOR_UPDATED'] = true;
48
- }
49
- }
50
- return $data;
51
- }
52
-
53
- // 2019 & 2020 & 2038
54
- static function postAuthorChanged($newAuthorID, $postID, $userID, $postTitle, $event, $quickFormEnabled = false)
55
- {
56
- $args = func_get_args();
57
- wpphLog(__METHOD__.'() triggered.',array('params'=> $args));
58
- if(empty($postID)){
59
- wpphLog('Error: $postID is empty. Invalid function call.');
60
- return false;
61
- }
62
- if(empty($newAuthorID)){
63
- wpphLog('Error: $newAuthorID is empty. Invalid function call.');
64
- return false;
65
- }
66
-
67
- global $wpdb;
68
- $oldAuthorID = $wpdb->get_var("SELECT post_author FROM ".$wpdb->posts." WHERE ID = ".$postID);
69
-
70
- wpphLog(__METHOD__.'() ',array(
71
- 'oldAuthorID' => $oldAuthorID,
72
- 'newAuthorID' => $newAuthorID
73
- ));
74
-
75
- if($newAuthorID <> $oldAuthorID)
76
- {
77
- $n = $wpdb->get_var("SELECT user_login FROM ".$wpdb->users." WHERE ID = ".$newAuthorID);
78
- $o = $wpdb->get_var("SELECT user_login FROM ".$wpdb->users." WHERE ID = ".$oldAuthorID);
79
-
80
- if($quickFormEnabled){
81
- // in quick edit form the authors get switched whereas in the default post editor they don't :/
82
- $t = $n;
83
- $n = $o;
84
- $o = $t;
85
- }
86
- $userID = (int)$userID;
87
- if(self::isCustomPost()){
88
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,ucfirst(self::$currentPostType),$n,$o));
89
- }
90
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,$n,$o)); }
91
- wpphLog(__METHOD__.' : Author updated.', array('from'=>$o, 'to'=>$n));
92
- return true;
93
- }
94
- return false;
95
- }
96
-
97
- // 2001 & 2005 & 2030
98
- static function newPostPublished($userID, $postTitle, $postUrl, $event)
99
- {
100
- if(self::isCustomPost()){
101
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst(self::$currentPostType), $postUrl));
102
- }
103
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,$postUrl)); }
104
- wpphLog(__METHOD__.'() : Post/Page published.', array('title'=>$postTitle));
105
- }
106
-
107
- // 2003 & 2007 & 2032
108
- static function draftPostUpdated($userID, $postID, $postTitle, $event)
109
- {
110
- if(self::isCustomPost()){
111
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst(self::$currentPostType), $postID));
112
- }
113
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,$postID)); }
114
- wpphLog(__METHOD__.'() : Draft post/page updated.', array('title'=>$postTitle));
115
- }
116
-
117
- // 2000 & 2004 & 2029
118
- static function newPostAsDraft($userID, $postID, $postTitle, $event)
119
- {
120
- if(self::isCustomPost()){
121
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst(self::$currentPostType), $postID));
122
- }
123
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, $postID)); }
124
- wpphLog(__METHOD__.'() : New post/page saved as draft.', array('title'=>$postTitle));
125
- }
126
-
127
- // 2017 & 2018
128
- static function postUrlUpdated($oldUrl, $newUrl, $userID, $postTitle, $event)
129
- {
130
- if($oldUrl == $newUrl) { return false; }
131
- if(self::isCustomPost()){
132
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst(self::$currentPostType), $oldUrl, $newUrl));
133
- }
134
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, $oldUrl, $newUrl)); }
135
- wpphLog(__METHOD__.'() : Post/Page URL updated.', array('from' => $oldUrl,'to' => $newUrl));
136
- return true;
137
- }
138
-
139
- // 2002 & 2006 & 2031
140
- static function publishedPostUpdated($userID, $postTitle, $postUrl, $event)
141
- {
142
- if(self::isCustomPost()){
143
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst(self::$currentPostType), $postUrl));
144
- }
145
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,$postUrl)); }
146
- wpphLog(__METHOD__.'() : Published post/page updated.', array('title'=>$postTitle));
147
- }
148
-
149
- static function postVisibilityChanged($userID, $postTitle, $fromVisibility, $toVisibility, $event)
150
- {
151
- if(self::isCustomPost()){
152
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,ucfirst(self::$currentPostType),$fromVisibility,$toVisibility));
153
- }
154
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,$fromVisibility,$toVisibility)); }
155
- wpphLog('Post visibility changed.', array('from' => $fromVisibility, 'to' => $toVisibility));
156
- }
157
-
158
- static function postDateChanged($userID, $postTitle, $fromDate, $toDate, $event)
159
- {
160
- $GLOBALS['WPPH_POST_DATE_CHANGED'] = true; // so we won't trigger the "modified post/page" event alongside the current event
161
- if(self::isCustomPost()){
162
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,ucfirst(self::$currentPostType),$fromDate,$toDate));
163
- }
164
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle,$fromDate,$toDate)); }
165
- wpphLog('Post date changed.', array('from' => $fromDate . ' ('.strtotime($fromDate).')', 'to' => $toDate . ' ('.strtotime($toDate).')'));
166
- }
167
-
168
- static function postStatusChanged($postTitle, $fromStatus, $toStatus, $userID, $event)
169
- {
170
- if(self::isCustomPost()){
171
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst(self::$currentPostType), $fromStatus, $toStatus));
172
- }
173
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, $fromStatus, $toStatus)); }
174
- wpphLog(__METHOD__.'() : Post status updated.', array('title'=>$postTitle, 'from' => $fromStatus, 'to' => $toStatus));
175
- }
176
-
177
- // 2016
178
- static function postCategoriesUpdated($userID, $postTitle, $fromCategories, $toCategories, $event)
179
- {
180
- if(self::isCustomPost()){
181
- WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, ucfirst(self::$currentPostType), $fromCategories, $toCategories));
182
- }
183
- else { WPPHEvent::_addLogEvent($event, $userID, WPPHUtil::getIP(), array($postTitle, $fromCategories, $toCategories)); }
184
- wpphLog(__METHOD__.' : Post categories updated.', array('from'=>$fromCategories, 'to'=>$toCategories));
185
- }
186
-
187
- static function isCustomPost(){
188
- if(in_array(self::$currentPostType, array('post','page'))){ return false; }
189
- return self::validatePostType(self::$currentPostType);
190
- }
191
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/WPPHUtil.php DELETED
@@ -1,371 +0,0 @@
1
- <?php
2
- class WPPHUtil
3
- {
4
- static function loadPluggable(){ @include_once(ABSPATH.'wp-includes/pluggable.php'); }
5
-
6
- static function getIP() { return(!empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0'); }
7
-
8
- /**
9
- * Check to see whether or not the current user is an administrator
10
- * @param int $userId
11
- * @return bool
12
- */
13
- static function isAdministrator($userId=0){ if(empty($userId)){$userId = wp_get_current_user();} return user_can($userId,'administrator'); }
14
-
15
- /**
16
- * Will respond to the ajax requests getting the events
17
- */
18
- static function get_events_html()
19
- {
20
- // VALIDATE REQUEST
21
- $rm = strtoupper($_SERVER['REQUEST_METHOD']);
22
- if($rm != 'POST'){ exit('<tr><td colspan="7"><span>'.__('Error: Invalid request',WPPH_PLUGIN_TEXT_DOMAIN).'</span></td></tr>'); }
23
-
24
- // set defaults
25
- $orderBy = 'EventNumber';
26
- $sort = 'desc';
27
- $limit = array(0, 50);
28
-
29
- if(!empty($_POST['orderBy'])) { $orderBy = $_POST['orderBy']; }
30
- if(!empty($_POST['sort'])) {
31
- if(0 == strcasecmp($_POST['sort'],'asc')){ $sort = 'asc'; }
32
- }
33
- if(isset($_POST['offset'])) { $limit[0] = intval($_POST['offset']); }
34
- if(isset($_POST['count'])) { $limit[1] = intval($_POST['count']); }
35
-
36
- global $current_user, $blog_id;
37
-
38
- $out = array();
39
- $out['events'] = array();
40
- $globalBlogID = $blog_id;
41
- $is_wpmu = WPPH::isMultisite();
42
- $isMainSite = WPPHUtil::isMainSite();
43
- $isSA = false;
44
- $allSites = false;
45
-
46
- $blogList = ($isMainSite && $is_wpmu) ? self::get_blogs() : array();
47
- function _getBlogName($id, $blogList) {
48
- for ($i = 0; $i < count($blogList); $i++) {
49
- if ($blogList[$i]['blog_id'] == $id) {
50
- return $blogList[$i]['blogname'];
51
- }
52
- }
53
- }
54
-
55
- if(!isset($_POST['blogID']) || empty($_POST['blogID'])){
56
- if($isMainSite){
57
- $postedBlogID = 0; // get the events for all sites by default
58
- $allSites = true;
59
- }
60
- else {
61
- $postedBlogID = $globalBlogID;
62
- $allSites = false;
63
- }
64
- }
65
- else { $postedBlogID = intval($_POST['blogID']); }
66
-
67
- if($is_wpmu){ $isSA = is_super_admin($current_user->ID); }
68
- else {
69
- if(empty($postedBlogID)){
70
- $postedBlogID = 1;
71
- }
72
- }
73
-
74
- if($is_wpmu && !$isSA)
75
- {
76
- // Only Super Admin can view other blogs' alerts
77
- if($globalBlogID <> $postedBlogID){
78
- $out['blogs'] = $blogList;
79
- exit( wpph_formatJsonOutput($out,__('There are no security alerts to display.',WPPH_PLUGIN_TEXT_DOMAIN)) );
80
- }
81
- }
82
-
83
- if(! $isMainSite){ $postedBlogID = $globalBlogID; }
84
-
85
- // get events
86
- if($allSites){
87
- $eventsCount = WPPHDB::getEventsCount(0);
88
- $events = WPPHEvent::getEvents($orderBy, $sort, $limit, 0);
89
- }
90
- else {
91
- $eventsCount = WPPHDB::getEventsCount($postedBlogID);
92
- $events = WPPHEvent::getEvents($orderBy, $sort, $limit, $postedBlogID);
93
- }
94
- $eventsNum = count($events);
95
-
96
- wpphLog("GETTING EVENTS FOR ".(empty($postedBlogID) ? 'ALL BLOGS' : 'BLOG: '.$postedBlogID).". Num events: $eventsNum");
97
-
98
- if($eventsNum == 0){
99
- if($is_wpmu){
100
- $out['blogs'] = $blogList;
101
- exit( wpph_formatJsonOutput($out,__('There are no security alerts to display.',WPPH_PLUGIN_TEXT_DOMAIN)) );
102
- }
103
- exit( wpph_formatJsonOutput(array(),__('There are no security alerts to display.',WPPH_PLUGIN_TEXT_DOMAIN)) );
104
- }
105
-
106
- // prepare output
107
- foreach($events as $entry)
108
- {
109
- $entry = (object)$entry;
110
- $eventNumber = $entry->EventNumber;
111
- $EventID = $entry->EventID;
112
- $EventDate = $entry->EventDate;
113
- $userIP = $entry->UserIP;
114
- $UserID = $entry->UserID;
115
- $blogId = $entry->BlogId;
116
- $eventData = ((!empty($entry->EventData)) ? unserialize(base64_decode($entry->EventData)) : ''); //<< values to use for event description
117
-
118
- $eventCount = intval($entry->EventCount);
119
- // get User Info
120
- if($UserID == 0){ $username = 'System'; }
121
- else {
122
- $user_info = get_userdata($UserID);
123
- $username = $user_info->user_login;
124
- $first_name = $user_info->user_firstname;
125
- $last_name = $user_info->user_lastname;
126
- $username = "$username ($first_name $last_name)";
127
- }
128
-
129
- // get event details
130
- $eventDetails = WPPHEvent::getEventDetailsData($EventID);
131
-
132
- // format event description message
133
- if($eventCount >=2 && $EventID == 1002){
134
- $evm = sprintf(__('<strong>%d</strong> failed login attempts from <strong>%s</strong> using <strong>%s</strong> as username.',WPPH_PLUGIN_TEXT_DOMAIN)
135
- , $eventCount, $userIP, base64_decode($entry->UserName));
136
- }
137
- else {
138
- if(empty($eventData)) { $evm = $eventDetails->EventDescription; }
139
- else { $evm = vsprintf($eventDetails->EventDescription, $eventData); }
140
- }
141
-
142
- $e = array(
143
- 'eventNumber' => $eventNumber,
144
- 'eventId' => $EventID,
145
- 'EventType' => $eventDetails->EventType,
146
- 'eventDate' => $EventDate,
147
- 'ip' => $userIP,
148
- 'user' => $username,
149
- 'siteName' => $allSites ? _getBlogName($blogId, $blogList) : '',
150
- 'description' => stripslashes($evm)
151
- );
152
- array_push($out['events'], $e);
153
- }
154
- $out['eventsCount'] = $eventsCount;
155
- $out['blogID'] = $postedBlogID;
156
- $out['blogs'] = $blogList;
157
- exit(wpph_formatJsonOutput($out));
158
- }
159
-
160
- // @since v0.6
161
- static function get_blogs()
162
- {
163
- if(wp_is_large_network()){
164
- return get_blog_details(get_option(WPPH_MAIN_SITE_ID_OPTION_NAME),true);
165
- }
166
- $blogs = wp_get_sites();
167
- $out = array();
168
- foreach($blogs as $blog){
169
- $entry = get_blog_details($blog['blog_id']);
170
- array_push($out, array(
171
- 'blog_id' => $entry->blog_id,
172
- 'blogname' => $entry->blogname
173
- ));
174
- }
175
- array_unshift($out, array('blog_id' => 0, 'blogname' => 'All sites'));
176
- return $out;
177
- }
178
-
179
- static function addDashboardWidget()
180
- {
181
- if(! empty(WPPH::getPluginSettings()->showDW))
182
- {
183
- $currentUser = wp_get_current_user();
184
- if(WPPHUtil::isAdministrator($currentUser->ID)|| WPPHUtil::isAllowedAccess($currentUser->ID) || WPPHUtil::isAllowedChange($currentUser->ID)){
185
- wp_add_dashboard_widget('wpphPluginDashboardWidget', __('Latest WordPress Security Alerts').' | WP Security Audit Log', array(get_class(),'createDashboardWidget'));
186
- }
187
- }
188
- }
189
- static function createDashboardWidget()
190
- {
191
- // get and display data
192
- $results = WPPHEvent::getEvents('EventNumber', 'DESC', array(0,5));
193
- echo '<div>';
194
- if(empty($results))
195
- {
196
- echo '<p>'.__('No alerts found.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
197
- }
198
- else {
199
- echo '<table class="wp-list-table widefat" cellspacing="0" cellpadding="0">';
200
- echo '<thead>';
201
- echo '<th class="manage-column" style="width: 15%;" scope="col">'.__('User',WPPH_PLUGIN_TEXT_DOMAIN).'</th>';
202
- echo '<th class="manage-column" style="width: 85%;" scope="col">'.__('Description',WPPH_PLUGIN_TEXT_DOMAIN).'</th>';
203
- echo '</thead>';
204
- echo '<tbody>';
205
- foreach($results as $entry)
206
- {
207
- $entry = (object)$entry;
208
- $eventID = $entry->EventID;
209
- $userID = $entry->UserID;
210
- $eventData = ((!empty($entry->EventData)) ? unserialize(base64_decode($entry->EventData)) : ''); //<< values to use for event description
211
- $eventCount = intval($entry->EventCount);
212
- $userIP = $entry->UserIP;
213
- // get User Info
214
- if($userID == 0){ $username = 'System'; }
215
- else {
216
- $user_info = get_userdata($userID);
217
- $username = $user_info->user_login;
218
- }
219
- // format event description message
220
- if($eventCount >=2 && $eventID == 1002){
221
- $evm = sprintf(__('<strong>%d</strong> failed login attempts from <strong>%s</strong> using <strong>%s</strong> as username.',WPPH_PLUGIN_TEXT_DOMAIN)
222
- , $eventCount, $userIP, base64_decode($entry->UserName));
223
- }
224
- else {
225
- $eventDetails = WPPHEvent::getEventDetailsData($eventID);
226
- if(empty($eventData)) { $evm = $eventDetails->EventDescription; }
227
- else { $evm = vsprintf($eventDetails->EventDescription, $eventData); }
228
- }
229
-
230
- echo '<tr>';
231
- echo '<td>'.$username.'</td>';
232
- echo '<td><a href="admin.php?page='.WPPH_PLUGIN_PREFIX.'">'.$evm.'</a></td>';
233
- echo '</tr>';
234
- }
235
- echo '</tbody>';
236
- echo '</table>';
237
- }
238
- echo '</div>';
239
- }
240
-
241
-
242
- /**
243
- * Check to see whether or not a user has access to view any of the plugin's pages
244
- * @since v0.5
245
- * @return bool
246
- */
247
- static function canViewPage()
248
- {
249
- $currentUser = wp_get_current_user();
250
- if(WPPHUtil::isAdministrator($currentUser->ID)|| WPPHUtil::isAllowedAccess($currentUser->ID) || WPPHUtil::isAllowedChange($currentUser->ID)){
251
- return true;
252
- }
253
- return false;
254
- }
255
-
256
- /**
257
- * Check to see whether or not the current user is allowed to VIEW the plugin
258
- * @since v0.5
259
- * @return bool
260
- */
261
- static function isAllowedAccess()
262
- {
263
- $data = WPPHNetwork::getGlobalOption(WPPH_PLUGIN_ALLOW_ACCESS_OPTION_NAME, true, true, array());
264
- if(empty($data)){return false;}
265
- $userID = wp_get_current_user()->ID;
266
- $userInfo = WPPHDB::getUserInfo($userID);
267
- return (in_array($userInfo['userName'], $data) || in_array($userInfo['userRole'], $data));
268
- }
269
- /**
270
- * Check to see whether or not the current user allowed to CHANGE the plugin's settings
271
- * @since v0.5
272
- * @return bool
273
- */
274
- static function isAllowedChange()
275
- {
276
- $data = WPPHNetwork::getGlobalOption(WPPH_PLUGIN_ALLOW_CHANGE_OPTION_NAME, true, true, array());
277
- if(empty($data)){return false;}
278
- $userID = wp_get_current_user()->ID;
279
- $userInfo = WPPHDB::getUserInfo($userID);
280
- return (in_array($userInfo['userName'], $data) || in_array($userInfo['userRole'], $data));
281
- }
282
- /**
283
- * @param array $data
284
- * @since v0.5
285
- * @return bool False if value was not updated and true if value was updated.
286
- */
287
- static function saveAllowAccessUserList(array $data)
288
- {
289
- $result = WPPHNetwork::updateGlobalOption(WPPH_PLUGIN_ALLOW_ACCESS_OPTION_NAME, $data, true, true);
290
- wpphLog(__METHOD__.'() result:', array('data'=> $data, 'result'=>$result));
291
- return $result;
292
- }
293
- /**
294
- * @param array $data
295
- * @since v0.5
296
- * @return bool False if value was not updated and true if value was updated.
297
- */
298
- static function saveAllowedChangeUserList(array $data)
299
- {
300
- $result = WPPHNetwork::updateGlobalOption(WPPH_PLUGIN_ALLOW_CHANGE_OPTION_NAME, $data, true, true);
301
- wpphLog(__METHOD__.'() result:', array('data'=> $data, 'result'=>$result));
302
- return $result;
303
- }
304
-
305
-
306
- /**
307
- * Saves the default list of users with access to plugin
308
- * @since v0.5
309
- * @return bool
310
- */
311
- static function saveInitialAccessChangeList()
312
- {
313
- if(self::isMainSite()){
314
- WPPHNetwork::addGlobalOption(WPPH_PLUGIN_ALLOW_ACCESS_OPTION_NAME, array(), true, true);
315
- WPPHNetwork::addGlobalOption(WPPH_PLUGIN_ALLOW_CHANGE_OPTION_NAME, array(), true, true);
316
- }
317
- }
318
-
319
- // ajax
320
- static function check_user_role()
321
- {
322
- // VALIDATE REQUEST
323
- $rm = strtoupper($_SERVER['REQUEST_METHOD']);
324
- if($rm != 'POST'){ exit(__('Error: Invalid request',WPPH_PLUGIN_TEXT_DOMAIN)); }
325
-
326
- $value = $_POST['check_input'];
327
-
328
- if(empty($value)){
329
- exit(__('Error: Invalid request',WPPH_PLUGIN_TEXT_DOMAIN));
330
- }
331
-
332
- $value = strtolower($value);
333
- $value = stripslashes($value);
334
- $value = strip_tags($value);
335
- $value = esc_sql($value);
336
-
337
- // check user
338
- $result = self::_userExists($value);
339
- if ($result){
340
- exit('1');
341
- }
342
- // check role
343
- $result = self::_roleExists($value);
344
- if ($result){
345
- exit('1');
346
- }
347
- exit('0');
348
- }
349
-
350
- static function _userExists($username){
351
- global $wpdb;
352
- $result = $wpdb->get_var($wpdb->prepare("SELECT `ID` FROM {$wpdb->users} WHERE user_login = '%s' OR display_name = '%s'", $username, $username));
353
- if ($result !== false && $result > 0){
354
- return true;
355
- }
356
- return false;
357
- }
358
-
359
- static function _roleExists($role){
360
- global $wp_roles;
361
- return (isset($wp_roles->roles[$role]) ? true : false);
362
- }
363
-
364
- static function isMainSite(){
365
- if (WPPH::isMultisite()) {
366
- global $current_site, $blog_id;
367
- return ($current_site->id == $blog_id);
368
- }
369
- return true;
370
- }
371
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/wpphFunctions.php DELETED
@@ -1,92 +0,0 @@
1
- <?php if(! defined('WPPH_PLUGIN_NAME')) return;
2
-
3
- /**
4
- * @param string $error The error to display in the admin notice
5
- * This function can be used to generate an admin notice error
6
- */
7
- function wpph_adminNotice($error) { echo '<div id="errMessage" class="error"><p><strong>'.WPPH_PLUGIN_NAME.' '.__('Error',WPPH_PLUGIN_TEXT_DOMAIN).':</strong> '.$error.'</p></div>'; }
8
- function wpph_adminUpdate($message) { echo '<div id="errMessage" class="updated"><p><strong>'.$message.'</strong></p></div>'; }
9
-
10
- add_action('wpph_set_post_type', 'wpph_setPostType', 1, 1);
11
- function wpph_setPostType($postType){
12
- WPPHPost::$currentPostType = $postType;
13
- wpphLog(__FUNCTION__.' triggered', array('postType'=>$postType));
14
- }
15
-
16
-
17
- /**
18
- * Retrieve the custom post type from the given base post type
19
- * @since v0.4
20
- * @param string $baseType The post's base type from which to extract the custom type
21
- * @return string The custom post type
22
- */
23
- function wpph_extractCustomPostType($baseType) { return substr($baseType, strpos($baseType,'-')+1); }
24
-
25
- /**
26
- * Check to see whether or not the provided event is enabled
27
- * @since v0.4
28
- * @param integer $event the event to search for
29
- * @param array $events Optional. The list of events where to search for $event to see if it's enabled or not
30
- * @return bool
31
- */
32
- function wpph_isEventEnabled($event, array $events = array())
33
- {
34
- if(empty($event)){ return false; }
35
- if(empty($events)){
36
- $events = wpph_getPluginEventsList();
37
- if(empty($events)){
38
- wpphLog("Error retrieving the list of events from database. option: ".WPPH_PLUGIN_EVENTS_LIST_OPTION_NAME." was either not found or empty.");
39
- return false;
40
- }
41
- }
42
- $event = (int)$event;
43
- foreach($events as $sections){
44
- foreach($sections as $_event => $enabled){
45
- if(($event == (int)$_event) && (bool)$enabled){
46
- return true;
47
- }
48
- }
49
- }
50
- return false;
51
- }
52
-
53
- // Add custom links on plugins page
54
- function wpphCustomLinks($links) { return array_merge(array('<a href="admin.php?page=wpph_">Audit Log Viewer </a>', '<a href="admin.php?page=wpph_settings">'.__('Settings',WPPH_PLUGIN_TEXT_DOMAIN).'</a>'), $links); }
55
- // Load text domain
56
- function wpphLoadTextDomain() { load_plugin_textdomain(WPPH_PLUGIN_TEXT_DOMAIN, false, 'wp-security-audit-log/languages/'); }
57
-
58
-
59
- /**
60
- * @internal
61
- * @param string $pluginName
62
- * @param int $userID
63
- * @param string $userIP
64
- */
65
- function wpph_installPlugin($pluginName, $userID, $userIP)
66
- {
67
- if(! empty($_GET['plugin']))
68
- {
69
- WPPHEvent::_addLogEvent(5000,$userID, $userIP, array($pluginName));
70
- wpphLog('Plugin installed.', array('plugin'=>$pluginName));
71
- }
72
- }
73
-
74
- function wpph_updatePluginEventsList($data)
75
- {
76
- if(WPPH::isMultisite()){
77
- update_blog_option((int)get_option(WPPH_MAIN_SITE_ID_OPTION_NAME), WPPH_PLUGIN_EVENTS_LIST_OPTION_NAME, $data);
78
- }
79
- else { update_option(WPPH_PLUGIN_EVENTS_LIST_OPTION_NAME, $data); }
80
- }
81
-
82
- function wpph_getPluginEventsList()
83
- {
84
- return WPPHNetwork::getGlobalOption(WPPH_PLUGIN_EVENTS_LIST_OPTION_NAME, true, true);
85
- }
86
-
87
- function wpph_formatJsonOutput(array $sourceData=array(), $error=''){
88
- return json_encode(array(
89
- 'dataSource' => $sourceData,
90
- 'error' => $error
91
- ));
92
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/wpphSettings.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
- define('WPPH_PLUGIN_VERSION_OPTION_NAME','WPPH_PLUGIN_VERSION');
3
- define('WPPH_PLUGIN_ERROR_OPTION_NAME','WPPH_PLUGIN_ERROR');
4
- define('WPPH_PLUGIN_SETTING_NAME', 'wpph_plugin_settings');
5
-
6
- define('WPPH_PLUGIN_DB_UPDATED', 'WPPH_PLUGIN_DB_UPDATED');
7
- define('WPPH_PLUGIN_DEL_EVENTS_CRON_TASK_NAME', 'wpph_plugin_delete_events_cron');
8
- /** @since v0.3 */
9
- define('WPPH_USERS_CAN_REGISTER_OPT_NAME', 'wpph_users_can_register');
10
- /**
11
- * @since v0.3
12
- * @see WPPH::onPluginActivate()
13
- */
14
- $GLOBALS['WPPH_CAN_RUN'] = true;
15
- /**@since 0.4*/
16
- define('WPPH_PLUGIN_TEXT_DOMAIN', 'wp-security-audit-log');
17
- /**@since 0.4*/
18
- define('WPPH_E_NOTICE_TEXT', __('NOTICE',WPPH_PLUGIN_TEXT_DOMAIN));
19
- /**@since 0.4*/
20
- define('WPPH_E_HIGH_TEXT', __('HIGH',WPPH_PLUGIN_TEXT_DOMAIN));
21
- /**@since 0.4*/
22
- define('WPPH_E_WARNING_TEXT', __('WARNING',WPPH_PLUGIN_TEXT_DOMAIN));
23
-
24
- /**@since 0.4*/
25
- define('WPPH_KEEP_MAX_EVENTS', 5000);
26
-
27
- //since v0.5
28
- define('WPPH_PLUGIN_ALLOW_ACCESS_OPTION_NAME','WPPH_PLUGIN_ALLOW_ACCESS');
29
- define('WPPH_PLUGIN_ALLOW_CHANGE_OPTION_NAME','WPPH_PLUGIN_ALLOW_CHANGE');
30
- define('WPPH_PLUGIN_EVENTS_LIST_OPTION_NAME', 'WPPH_PLUGIN_EVENTS_LIST');
31
-
32
- //since v0.6
33
- define('WPPH_NETWORK_ID_OPTION_NAME','WPPH_NETWORK_ID');
34
- define('WPPH_MAIN_SITE_ID_OPTION_NAME','WPPH_MAIN_SITE_ID');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/auditlog.js ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var WsalData;
2
+
3
+ function WsalAuditLogInit(_WsalData){
4
+ WsalData = _WsalData;
5
+ var WsalTkn = WsalData.autorefresh.token;
6
+
7
+ // list refresher
8
+ var WsalAjx = null;
9
+ var WsalChk = function(){
10
+ if(WsalAjx)WsalAjx.abort();
11
+ WsalAjx = jQuery.post(WsalData.ajaxurl, {
12
+ action: 'AjaxRefresh',
13
+ logcount: WsalTkn
14
+ }, function(data){
15
+ WsalAjx = null;
16
+ if(data && data !== 'false'){
17
+ WsalTkn = data;
18
+ jQuery('#audit-log-viewer').load(location.href + ' #audit-log-viewer');
19
+ }
20
+ WsalChk();
21
+ });
22
+ };
23
+ if(WsalData.autorefresh.enabled){
24
+ setInterval(WsalChk, 40000);
25
+ WsalChk();
26
+ }
27
+ }
28
+
29
+ var WsalIppsPrev;
30
+
31
+ function WsalIppsFocus(value){
32
+ WsalIppsPrev = value;
33
+ }
34
+
35
+ function WsalIppsChange(value){
36
+ if(value === ''){
37
+ value = window.prompt(WsalData.tr8n.numofitems, WsalIppsPrev);
38
+ if(value === null || value === WsalIppsPrev)return this.value = WsalIppsPrev; // operation canceled
39
+ }
40
+ jQuery('select.wsal-ipps').attr('disabled', true);
41
+ jQuery.post(WsalData.ajaxurl, {
42
+ action: 'AjaxSetIpp',
43
+ count: value
44
+ }, function(){
45
+ location.reload();
46
+ });
47
+ }
48
+
49
+ function WsalSsasChange(value){
50
+ jQuery('select.wsal-ssas').attr('disabled', true);
51
+ jQuery('#wsal-cbid').val(value);
52
+ jQuery('#audit-log-viewer').submit();
53
+ }
js/nice_r.js ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function nice_r_toggle(pfx, id){
2
+ var el = document.getElementById(pfx+'_v'+id);
3
+ if(el){
4
+ if(el.style.display==='block'){
5
+ el.style.display = 'none';
6
+ document.getElementById(pfx+'_a'+id).innerHTML = '&#9658;';
7
+ }else{
8
+ el.style.display = 'block';
9
+ document.getElementById(pfx+'_a'+id).innerHTML = '&#9660;';
10
+ }
11
+ }
12
+ }
js/settings.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function(){
2
+ var RemoveSecToken = function(){
3
+ var $this = jQuery(this).parents('span:first');
4
+ $this.addClass('sectoken-del').fadeOut('fast', function(){
5
+ $this.remove();
6
+ });
7
+ };
8
+
9
+ jQuery('#ViewerQueryBox, #EditorQueryBox').keydown(function(event){
10
+ if(event.keyCode === 13) {
11
+ var type = jQuery(this).attr('id').substr(0, 6);
12
+ jQuery('#'+type+'QueryAdd').click();
13
+ return false;
14
+ }
15
+ });
16
+
17
+ jQuery('#ViewerQueryAdd, #EditorQueryAdd').click(function(){
18
+ var type = jQuery(this).attr('id').substr(0, 6);
19
+ var value = jQuery.trim(jQuery('#'+type+'QueryBox').val());
20
+ var existing = jQuery('#'+type+'List input').filter(function() { return this.value === value; });
21
+
22
+ if(!value || existing.length)return; // if value is empty or already used, stop here
23
+
24
+ jQuery('#'+type+'QueryBox, #'+type+'QueryAdd').attr('disabled', true);
25
+ jQuery.post(jQuery('#ajaxurl').val(), {action: 'AjaxCheckSecurityToken', token: value}, function(data){
26
+ jQuery('#'+type+'QueryBox, #'+type+'QueryAdd').attr('disabled', false);
27
+ if(data==='other' && !confirm('The specified token is not a user nor a role, do you still want to add it?'))return;
28
+ jQuery('#'+type+'QueryBox').val('');
29
+ jQuery('#'+type+'List').append(jQuery('<span class="sectoken-'+data+'"/>').text(value).append(
30
+ jQuery('<input type="hidden" name="'+type+'s[]"/>').val(value),
31
+ jQuery('<a href="javascript:;" title="Remove">&times;</a>').click(RemoveSecToken)
32
+ ));
33
+ });
34
+ });
35
+
36
+ jQuery('#ViewerList>span>a, #EditorList>span>a').click(RemoveSecToken);
37
+ });
pages/about.php DELETED
@@ -1,21 +0,0 @@
1
- <?php if(! WPPHUtil::canViewPage()){ return; } ?>
2
- <?php
3
- if(! WPPH::ready())
4
- {
5
- $errors = WPPH::getPluginErrors();
6
- foreach($errors as $error) {
7
- wpph_adminNotice(base64_decode($error));
8
- }
9
- echo '<div id="wpph-pageWrapper" class="wrap">';
10
- echo '<p>'.__('We have encountered some errors during the installation of the plugin which you can find above.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
11
- echo '<p>'.__('Please try to correct them and then reactivate the plugin.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
12
- echo '</div>';
13
- return;
14
- }
15
- ?>
16
- <div id="wpph-pageWrapper" class="wrap">
17
- <h2 class="pageTitle pageTitle-about"><?php echo __('About us',WPPH_PLUGIN_TEXT_DOMAIN);?></h2>
18
- <div>
19
- <p><?php echo sprintf(__('WP Security Audit Log is a WordPress security plugin developed by %s.',WPPH_PLUGIN_TEXT_DOMAIN), '<a href="http://www.wpwhitesecurity.com">WP White Security</a>');?></p>
20
- </div>
21
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/alerts.php DELETED
@@ -1,190 +0,0 @@
1
- <?php if(! WPPHUtil::canViewPage()){ return; } ?>
2
- <?php
3
- if(! WPPH::ready())
4
- {
5
- $errors = WPPH::getPluginErrors();
6
- foreach($errors as $error) {
7
- wpph_adminNotice($error);
8
- }
9
- echo '<div id="wpph-pageWrapper" class="wrap">';
10
- echo '<p>'.__('We have encountered some errors during the installation of the plugin which you can find above.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
11
- echo '<p>'.__('Please try to correct them and then reactivate the plugin.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
12
- echo '</div>';
13
- return;
14
- }
15
- ?>
16
- <?php
17
- // defaults
18
- $defaultEvents = WPPH::getDefaultEvents();
19
- $logEvents = WPPH::getEvents();
20
- $validationMessage = array();
21
- $sectionNames = (empty($logEvents)) ? array(): array_keys($logEvents);
22
- $activeTab = 0;
23
- $rm = strtoupper($_SERVER['REQUEST_METHOD']);
24
- if('POST' == $rm)
25
- {
26
- // Check nonce
27
- if(isset($_POST['wpph_update_settings_field_nonce'])){
28
- if(!wp_verify_nonce($_POST['wpph_update_settings_field_nonce'],'wpph_update_settings')){
29
- wp_die('Invalid request');
30
- }
31
- }
32
- else {wp_die('Invalid request');}
33
-
34
- $hasErrors = false;
35
- $activeTab = intval($_POST['activeTab']);
36
- $inputEvents = $_POST['inputEvents'];
37
- $inputEvents = str_replace("\\", "",$inputEvents);
38
- $inputEvents = json_decode($inputEvents, true);
39
- if(is_null($inputEvents)){
40
- $validationMessage['error'] = __('JSON Decode Error: ',WPPH_PLUGIN_TEXT_DOMAIN).json_last_error();
41
- $hasErrors = true;
42
- }
43
-
44
- // save options
45
- if(!$hasErrors)
46
- {
47
- $logEvents = array();
48
- $wpphEvents = WPPH::getEvents();
49
- foreach($wpphEvents as $category=>$entries){
50
- $logEvents[$category] = array();
51
- foreach($entries as $event=>$entry){
52
- $logEvents[$category][$event] = 1;
53
- }
54
- }
55
- foreach($inputEvents as $category=>$entries){
56
- foreach($entries as $entry){
57
- $event = (int)$entry['e'];
58
- // validate event before insert
59
- if(isset($wpphEvents[$category][$event])){
60
- $logEvents[$category][$event] = 0;
61
- }
62
- }
63
- }
64
- wpph_updatePluginEventsList($logEvents);
65
- $validationMessage['success'] = __('Your settings have been saved.',WPPH_PLUGIN_TEXT_DOMAIN);
66
- }
67
- }
68
- // end $post
69
- ?>
70
- <div id="wpph-pageWrapper" class="wrap">
71
- <h2 class="pageTitle pageTitle-settings"><?php echo __('Enable/Disable Alerts',WPPH_PLUGIN_TEXT_DOMAIN);?></h2>
72
-
73
- <div id="optionsDescription">
74
- <p id="description" style="background: none repeat scroll 0 0 #EEEEEE;border: 1px solid #AAAAAA;border-radius: 4px 4px 4px 4px;box-shadow: 2px 2px 3px #DDDDDD;">
75
- <?php
76
- echo __('From this page you can enable or disable WordPress security alerts. If a security alert is disabled, an alert will not be generated in the Audit Log Viewer once such action happens.',WPPH_PLUGIN_TEXT_DOMAIN);
77
- echo '<br/>'.__('To disable a security alert, select the category tab and untick the alert. Click Save Settings when ready.',WPPH_PLUGIN_TEXT_DOMAIN);
78
- ?>
79
- </p>
80
- </div>
81
-
82
- <?php if(! empty($validationMessage)) : ?>
83
- <?php
84
- if(!empty($validationMessage['error'])){ wpph_adminNotice($validationMessage['error']); }
85
- else { wpph_adminUpdate($validationMessage['success']); }
86
- ?>
87
- <?php endif;?>
88
-
89
- <?php if(!empty($sectionNames)) : ?>
90
- <div id="logEventsTabControl" style="margin: 20px 0; opacity: 0;">
91
- <form id="updateSettingsForm" method="post">
92
- <?php wp_nonce_field('wpph_update_settings','wpph_update_settings_field_nonce'); ?>
93
- <?php
94
- echo '<ul id="tabControlNavBar">';
95
- foreach($sectionNames as $item){
96
- echo '<li data-id="'.$item.'"><a href="#'.$item.'"/>'.str_replace('_',' ', $item).'</a></li>';
97
- }
98
- echo '</ul>';
99
-
100
- foreach($logEvents as $sectionName => $items){
101
- echo '<div id="'.$sectionName.'">';
102
- echo '<table class="wp-list-table widefat" cellspacing="0" cellpadding="0">';
103
- echo '<thead>';
104
- echo '<th class="manage-column column-cb check-column item-cb item-cb_h" scope="col"><input type="checkbox" class="js-select-all"/></th>';
105
- echo '<th class="manage-column item-event" scope="col">'.__('Event',WPPH_PLUGIN_TEXT_DOMAIN).'</th>';
106
- echo '<th class="manage-column item-type" scope="col">'.__('Type',WPPH_PLUGIN_TEXT_DOMAIN).'</th>';
107
- echo '<th class="manage-column item-description" scope="col">'.__('Description',WPPH_PLUGIN_TEXT_DOMAIN).'</th>';
108
- echo '</thead>';
109
- echo '<tbody>';
110
- $disabledEvents = array(6001,6002);
111
- foreach($items as $item => $enabled){
112
- if(in_array((int)$item, $disabledEvents)){
113
- $t = sprintf(__('Event %s is not available in MultiSite.',WPPH_PLUGIN_TEXT_DOMAIN), $item);
114
- echo '<tr class="row" title="'.$t.'">';
115
- echo '<th class="manage-column column-cb check-column item-cb_h" scope="row"><input class="item_cb" type="checkbox" disabled="disabled"/></th>';
116
- echo '<td class="wpph-text-disabled">'.$item.'</td>';
117
- echo '<td class="wpph-text-disabled">'.(isset($defaultEvents[$sectionName][$item]['type']) ? $defaultEvents[$sectionName][$item]['type'] : '').'</td>';
118
- echo '<td class="wpph-text-disabled">'.(isset($defaultEvents[$sectionName][$item]['text']) ? $defaultEvents[$sectionName][$item]['text'] : '').'</td>';
119
- echo '</tr>';
120
- }
121
- else {
122
- echo '<tr class="row">';
123
- echo '<th class="manage-column column-cb check-column item-cb_h" scope="row"><input class="item_cb" type="checkbox" '.($enabled ? 'checked="checked"' : '').' value="'.$item.'"/></th>';
124
- echo '<td>'.$item.'</td>';
125
- echo '<td>'.(isset($defaultEvents[$sectionName][$item]['type']) ? $defaultEvents[$sectionName][$item]['type'] : '').'</td>';
126
- echo '<td>'.(isset($defaultEvents[$sectionName][$item]['text']) ? $defaultEvents[$sectionName][$item]['text'] : '').'</td>';
127
- echo '</tr>';
128
-
129
- }
130
- }
131
- echo '</tbody>';
132
- echo '</table>';
133
- echo '</div>';
134
- }
135
- ?>
136
- <input type="submit" id="submitButton" class="button button-primary" style="margin: 0 0 12px 19px;" value="<?php echo __('Save settings',WPPH_PLUGIN_TEXT_DOMAIN);?>"/>
137
- <input type="hidden" id="inputEvents" name="inputEvents" value=""/>
138
- <input type="hidden" id="activeTab" name="activeTab" value=""/>
139
- </form>
140
- </div>
141
- <?php else : ?>
142
- <div class="error"><p><?php echo __('Error retrieving the list of events from database. Please inform the plugin author about this.',WPPH_PLUGIN_TEXT_DOMAIN);?></p></div>
143
- <?php endif; ?>
144
- </div>
145
- <br class="clear"/>
146
-
147
- <script type="text/javascript">
148
- jQuery(document).ready(function($){
149
- var tabControl = $('#logEventsTabControl');
150
- var activeTab = $('#activeTab');
151
- tabControl.tabs();
152
- tabControl.tabs("option", "active", <?php echo $activeTab;?>);
153
- tabControl.css('opacity',1);
154
- // update select all checkbox
155
- $('#tabControlNavBar li').each(function(){
156
- var sectionName = $(this).data('id');
157
- if(sectionName.length > 0){
158
- $('#'+sectionName+' input:checkbox.item_cb').each(function() {
159
- var self = $(this);
160
- if (self.prop('checked')) {
161
- $('#'+sectionName+' input:checkbox.js-select-all').attr('checked','checked');
162
- }
163
- });
164
- }
165
- });
166
- //
167
- // form submit
168
- $('#submitButton').on('click',function()
169
- {
170
- activeTab.val(tabControl.tabs("option","active"));
171
- // build options
172
- var e = $('#inputEvents')
173
- ,catList = $('#tabControlNavBar li')
174
- ,outData = {};
175
- catList.each(function(){
176
- var sectionName = $(this).data('id');
177
- if(sectionName.length > 0){
178
- outData[sectionName] = [];
179
- $('#'+sectionName+' input:checkbox.item_cb').each(function() {
180
- var self = $(this);
181
- if (!self.prop('checked')) {
182
- outData[sectionName].push({"e": self.val()});
183
- }
184
- });
185
- }
186
- });
187
- e.val(JSON.stringify(outData));
188
- });
189
- });
190
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/dashboard.php DELETED
@@ -1,165 +0,0 @@
1
- <?php if(! WPPHUtil::canViewPage()){ return; } ?>
2
- <?php
3
- if(! WPPH::ready())
4
- {
5
- $errors = WPPH::getPluginErrors();
6
- foreach($errors as $error) {
7
- wpph_adminNotice($error);
8
- }
9
- echo '<div id="wpph-pageWrapper" class="wrap">';
10
- echo '<p>'.__('We have encountered some errors during the installation of the plugin which you can find above.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
11
- echo '<p>'.__('Please try to correct them and then reactivate the plugin.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
12
- echo '</div>';
13
- return;
14
- }
15
- ?>
16
- <div id="wpph-pageWrapper" class="wrap">
17
- <h2 class="pageTitle pageTitle-eventViewer">Audit Log Viewer</h2>
18
- <div id="EventViewerWrapper">
19
- <div style="overflow: hidden; display: block; clear: both;">
20
- <div class="tablenav top" style="overflow: hidden; padding: 4px 0;">
21
- <div class="alignleft">
22
- <div style="overflow: hidden;">
23
- <input type="button" class="buttonRefreshEventsList button" value="<?php echo __('Refresh Security Alerts List',WPPH_PLUGIN_TEXT_DOMAIN);?>"
24
- style="float: left; display: block;" data-bind="disable: loading, click: $root.onRefreshEvents"/>
25
- <span class="ajaxLoaderWrapper" style="float: left; display: block; width: 20px; height: 20px; padding: 7px 7px;"><img/></span>
26
- </div>
27
- </div>
28
- <div class="alignleft actions" style="overflow: hidden;">
29
- <label class="alignleft" style="margin: 5px 5px 0 0;"><?php echo __('Number of security alerts per page:',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
30
- <select name="actionLimit1" class="actionLimit" data-bind="options: availablePageSize, value: selectedPageSize, disable: loading"></select>
31
- <input type="button" value="Apply" class="button action" data-bind="disable: loading, click: $root.onApplyPageSize">
32
- </div>
33
-
34
- <?php if(WPPH::isMultisite()): ?>
35
- <!-- ko if: isMainSite -->
36
- <div class="alignleft" style="padding: 2px 0;">
37
- <select id="ntwkBlogs" data-bind="disable: loading, options: blogList, optionsText: 'blogname', optionsValue: 'blog_id', value: blogId"></select>
38
- </div>
39
- <!-- /ko -->
40
- <?php endif;?>
41
-
42
- <div class="tablenav-pages">
43
- <span class="displaying-num" data-bind="text: totalEventsCount()+' security alerts'"></span>
44
- <span class="pagination-links"><a href="#" title="Go to the first page" class="first-page" data-bind="click: firstPage, css: {disabled: offset() <= 0}">«</a>
45
- <a href="#" title="Go to the previous page" class="prev-page" data-bind="click: prevPage, disable: loading, click: prevPage, css: {disabled: offset() <= 0}">‹</a>
46
- <span class="paging-input">
47
- <input type="text" size="1" id="fdr" title="Current page" class="current-page"
48
- data-bind="value: currentPage, event: {keydown: onCurrentPageInputKeyDown} "/> of <span class="total-pages" data-bind="text: pageCount"></span>
49
- </span>
50
- <a href="#" title="Go to the next page" class="next-page" data-bind="click: nextPage, disable: loading, click: nextPage, css: {disabled: offset() + events().length >= totalEventsCount() - 1}">›</a>
51
- <a href="#" title="Go to the last page" class="last-page" data-bind="click: lastPage, css: {disabled: offset() + events().length >= totalEventsCount() - 1}">»</a></span>
52
- </div>
53
-
54
- </div>
55
- </div>
56
- <table class="wp-list-table widefat fixed" cellspacing="0" cellpadding="0">
57
- <thead>
58
- <tr data-bind="foreach: { data: columns, as: 'columnItem' }">
59
- <!-- ko if: columnItem.visible -->
60
- <th class="manage-column column-left-align" scope="col"
61
- data-bind="style: {width: columnItem.columnWidth}, css: { sortable: columnItem.sortable, sorted: columnItem.sorted, desc: columnItem.sortable && columnItem.sortedDescending(), asc: columnItem.sortable && !columnItem.sortedDescending()}">
62
- <a href="#" data-bind="disable: $root.loading, click: columnItem.sortable ? $root.onApplySorting : function() { return false; }">
63
- <span data-bind="text: columnItem.columnHeader"></span>
64
- <span class="sorting-indicator"></span>
65
- </a>
66
- </th>
67
- <!-- /ko -->
68
- </tr>
69
- </thead>
70
- <tfoot>
71
- <tr data-bind="foreach: { data: columns, as: 'columnItem' }">
72
- <!-- ko if: columnItem.visible -->
73
- <th class="manage-column column-left-align" scope="col"
74
- data-bind="style: {width: columnItem.columnWidth}, css: { sortable: columnItem.sortable, sorted: columnItem.sorted, desc: columnItem.sortable && columnItem.sortedDescending(), asc: columnItem.sortable && !columnItem.sortedDescending()}">
75
- <a href="#" data-bind="disable: $root.loading, click: columnItem.sortable ? $root.onApplySorting : function() { return false; }">
76
- <span data-bind="text: columnItem.columnHeader"></span>
77
- <span class="sorting-indicator"></span>
78
- </a>
79
- </th>
80
- <!-- /ko -->
81
- </tr>
82
- </tfoot>
83
- <tbody id="the-list">
84
- <!-- ko if: events().length == 0 -->
85
- <tr>
86
- <!-- ko ifnot: error -->
87
- <td id="wpph_ew" style="padding: 4px !important;" colspan="8"><?php echo __('No security alerts',WPPH_PLUGIN_TEXT_DOMAIN);?></td>
88
- <!-- /ko -->
89
-
90
- <!-- ko if: error -->
91
- <td id="wpph_ew" style="padding: 4px !important;" colspan="8">
92
- <strong data-bind="text: error"></strong>
93
- </td>
94
- <!-- /ko -->
95
- </tr>
96
- <!-- /ko -->
97
-
98
- <!-- ko foreach: events -->
99
- <tr data-bind="css: {'row-0': ($index() % 2) == 0, 'row-1': ($index() % 2) != 0}">
100
- <td class="column-event_number"><span data-bind="text: eventNumber"></span></td>
101
- <td class="column-event_id"><span data-bind="text: eventId"></span></td>
102
- <td class="column-event_date"><span data-bind="text: eventDate"></span></td>
103
- <td class="column-event_category"><span data-bind="text: EventType"></span></td>
104
- <td class="column-ip"><span data-bind="text: ip"></span></td>
105
- <td class="column-user"><span data-bind="text: user"></span></td>
106
- <!-- ko if: $root.blogId() === 0 -->
107
- <td class="column-site"><span data-bind="text: $data.siteName"></span></td>
108
- <!-- /ko -->
109
- <td class="column-description"><span data-bind="html: description"></span></td>
110
- </tr>
111
- <!-- /ko -->
112
- </tbody>
113
- </table>
114
- <div style="overflow: hidden; display: block; clear: both;">
115
- <div class="tablenav top" style="overflow: hidden; padding: 4px 0;">
116
- <div class="alignleft">
117
- <div style="overflow: hidden;">
118
- <input type="button" class="buttonRefreshEventsList button" value="<?php echo __('Refresh security alerts List',WPPH_PLUGIN_TEXT_DOMAIN);?>"
119
- style="float: left; display: block;" data-bind="disable: loading, click: $root.onRefreshEvents"/>
120
- <span class="ajaxLoaderWrapper" style="float: left; display: block; width: 20px; height: 20px; padding: 7px 7px;"><img/></span>
121
- </div>
122
- </div>
123
- <div class="alignleft actions" style="overflow: hidden;">
124
- <label class="alignleft" style="margin: 5px 5px 0 0;"><?php echo __('Number of security alerts per page:',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
125
- <select name="actionLimit1" class="actionLimit" data-bind="options: availablePageSize, value: selectedPageSize, disable: loading"></select>
126
- <input type="button" value="Apply" class="button action" data-bind="disable: loading, click: $root.onApplyPageSize">
127
- </div>
128
- <div class="tablenav-pages">
129
- <span class="displaying-num" data-bind="text: totalEventsCount()+' security alerts'"></span>
130
- <span class="pagination-links"><a href="#" title="Go to the first page" class="first-page" data-bind="click: firstPage, css: {disabled: offset() <= 0}">«</a>
131
- <a href="#" title="Go to the previous page" class="prev-page" data-bind="click: prevPage, disable: loading, click: prevPage, css: {disabled: offset() <= 0}">‹</a>
132
- <span class="paging-input">
133
- <input type="text" size="1" id="fdr" title="Current page" class="current-page"
134
- data-bind="value: currentPage, event: {keydown: onCurrentPageInputKeyDown} "/> of <span class="total-pages" data-bind="text: pageCount"></span>
135
- </span>
136
- <a href="#" title="Go to the next page" class="next-page" data-bind="click: nextPage, disable: loading, click: nextPage, css: {disabled: offset() + events().length >= totalEventsCount() - 1}">›</a>
137
- <a href="#" title="Go to the last page" class="last-page" data-bind="click: lastPage, css: {disabled: offset() + events().length >= totalEventsCount() - 1}">»</a></span>
138
- </div>
139
- </div>
140
- </div>
141
-
142
- </div>
143
- </div>
144
- <?php global $blog_id; ?>
145
- <script type="text/javascript">
146
- jQuery.WPPH_CRT_BLOG_ID = <?php echo WPPHUtil::isMainSite() ? 0 : $blog_id;?>;
147
- // configure ajax loader
148
- var __ajaxLoaderTargetElement__ = jQuery('.ajaxLoaderWrapper img');
149
- var AjaxLoaderCreate = function(e){
150
- var imgPath = "<?php echo WPPH_PLUGIN_URL.'res/img/ajax-loader.gif';?>";
151
- e.attr('src',imgPath);
152
- }(__ajaxLoaderTargetElement__);
153
- var AjaxLoaderShow = function(e){ e.show(); };
154
- var AjaxLoaderHide = function(e){ e.hide(); };
155
- jQuery(document).ready(function($) {
156
- var myViewModel = AuditLogViewModel;
157
- myViewModel.isMainSite(!!<?php echo WPPHUtil::isMainSite() ? '1': '0';?>);
158
- myViewModel.orderBy('EventNumber');
159
- myViewModel.orderByDescending(true);
160
- ko.applyBindings(myViewModel, $('#wpph-pageWrapper').get(0));
161
- setTimeout(function() {
162
- myViewModel.refreshEvents(0, jQuery.WPPH_CRT_BLOG_ID);
163
- });
164
- });
165
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/settings.php DELETED
@@ -1,576 +0,0 @@
1
- <?php //if(! WPPHUtil::canViewPage()){ return; } ?>
2
- <?php
3
- if(! WPPH::ready())
4
- {
5
- $errors = WPPH::getPluginErrors();
6
- foreach($errors as $error) {
7
- wpph_adminNotice($error);
8
- }
9
- echo '<div id="wpph-pageWrapper" class="wrap">';
10
- echo '<p>'.__('We have encountered some errors during the installation of the plugin which you can find above.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
11
- echo '<p>'.__('Please try to correct them and then reactivate the plugin.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
12
- echo '</div>';
13
- return;
14
- }
15
- ?>
16
- <?php
17
- // defaults
18
- $opt = WPPH::getPluginSettings();
19
- $daysInput = 0;
20
- $eventsNumber = 0;
21
- $validationMessage = array();
22
- $hasErrors = false;
23
- $showDW = (empty($opt->showDW) ? false : true);
24
- // active delete option for events
25
- if(!empty($opt->daysToKeep)){ $daysInput = $opt->daysToKeep; $activeOption = 1; }
26
- if(! empty($opt->eventsToKeep)){ $eventsNumber = $opt->eventsToKeep; $activeOption = 2; }
27
- $allowedAccess = WPPHNetwork::getGlobalOption(WPPH_PLUGIN_ALLOW_ACCESS_OPTION_NAME, true, true, array());
28
- $allowedChange = WPPHNetwork::getGlobalOption(WPPH_PLUGIN_ALLOW_CHANGE_OPTION_NAME, true, true, array());
29
- if(! isset($activeOption)){ $activeOption = 2; }
30
- // end defaults
31
-
32
- $rm = strtoupper($_SERVER['REQUEST_METHOD']);
33
- if('POST' == $rm)
34
- {
35
- // Check nonce
36
- if(isset($_POST['wpph_update_settings_field_nonce'])){
37
- if(!wp_verify_nonce($_POST['wpph_update_settings_field_nonce'],'wpph_update_settings')){
38
- wp_die('Invalid request');
39
- }
40
- }
41
- else {wp_die('Invalid request');}
42
-
43
- // method to use
44
- if(! isset($_POST['deleteEventsBy'])){ wp_die('Invalid request'); }
45
- // value to use
46
- if(! isset($_POST['deleteEventsValue'])){ wp_die('Invalid request'); }
47
- $deleteEventsBy = intval($_POST['deleteEventsBy']);
48
- $deleteEventsValue = intval($_POST['deleteEventsValue']);
49
-
50
- $pac = (isset($_POST['accessListInput']) ? trim($_POST['accessListInput']) : null);
51
- $pcc = (isset($_POST['changeListInput']) ? trim($_POST['changeListInput']) : null);
52
-
53
- // pre-validate access rules if any
54
- if(! empty($pac) && strlen($pac)>4){
55
- $pac = str_replace('\\','', $pac);
56
- $allowedAccess = json_decode($pac);
57
- if(is_null($allowedAccess)){
58
- wpphLog('Error decoding json input $pac.', array('pac'=>$pac));
59
- $validationMessage['error'] = __('UAL: Error decoding json input', WPPH_PLUGIN_TEXT_DOMAIN);
60
- $hasErrors = true;
61
- }
62
- else {
63
- wpphLog('accessListInput is not empty.', array('pac'=>$pac,'data'=>$allowedAccess));
64
- WPPHUtil::saveAllowAccessUserList($allowedAccess);
65
- }
66
- }
67
- else {
68
- wpphLog('accessListInput is empty. Resetting the user access list.');
69
- WPPHUtil::saveAllowAccessUserList(array());
70
- }
71
-
72
- // pre-validate change rules if any
73
- if(! empty($pcc) && strlen($pcc)>4){
74
- $pcc = str_replace('\\','', $pcc);
75
- $allowedChange = json_decode($pcc);
76
- if(is_null($allowedChange)){
77
- wpphLog('Error decoding json input $pcc.', array('pcc'=>$pcc));
78
- $validationMessage['error'] = __('UCL: Error decoding json input: '.$pcc.' '.var_export($allowedChange,true), WPPH_PLUGIN_TEXT_DOMAIN);
79
- $hasErrors = true;
80
- }
81
- else {
82
- wpphLog('changeListInput is not empty.', array('pcc'=>$pcc,'data'=>$allowedChange));
83
- WPPHUtil::saveAllowedChangeUserList($allowedChange);
84
- }
85
- }
86
- else {
87
- wpphLog('changeListInput is empty. Resetting the user access list.');
88
- WPPHUtil::saveAllowedChangeUserList(array());
89
- }
90
-
91
- // if Delete events older than ... days
92
- if($deleteEventsBy == 1)
93
- {
94
- $activeOption = 1;
95
- $daysInput = $deleteEventsValue;
96
-
97
- // Validate
98
- if(!preg_match('/^\d+$/',$deleteEventsValue)){
99
- $validationMessage['error'] = __('Incorrect number of days. Please specify a value between 1 and 365.',WPPH_PLUGIN_TEXT_DOMAIN);
100
- $hasErrors = true;
101
- }
102
- elseif($deleteEventsValue < 1 || $deleteEventsValue > 365){
103
- $validationMessage['error'] = __('Incorrect number of days. Please specify a value between 1 and 365.',WPPH_PLUGIN_TEXT_DOMAIN);
104
- $hasErrors = true;
105
- }
106
- else {
107
- if(! $hasErrors){
108
- // reset events number
109
- if(isset($opt->eventsToKeep)){
110
- $opt->eventsToKeep = 0;
111
- }
112
- $opt->daysToKeep = $deleteEventsValue;
113
- }
114
- }
115
- }
116
- elseif($deleteEventsBy == 2)
117
- {
118
- $activeOption = 2;
119
- $eventsNumber = $deleteEventsValue;
120
-
121
- // Validate
122
- if(!preg_match('/^\d+$/',$deleteEventsValue)){
123
- $validationMessage['error'] = sprintf(__('Incorrect number of security alerts. Please specify a value between 1 and %d.',WPPH_PLUGIN_TEXT_DOMAIN), WPPH_KEEP_MAX_EVENTS);
124
- $hasErrors = true;
125
- }
126
- elseif($deleteEventsValue < 1 || $deleteEventsValue > WPPH_KEEP_MAX_EVENTS){
127
- $validationMessage['error'] = sprintf(__('Incorrect number of security alerts. Please specify a value between 1 and %d.',WPPH_PLUGIN_TEXT_DOMAIN), WPPH_KEEP_MAX_EVENTS);
128
- $hasErrors = true;
129
- }
130
- else {
131
- // reset days
132
- if(isset($opt->daysToKeep)){
133
- $opt->daysToKeep = 0;
134
- }
135
- $opt->eventsToKeep = $deleteEventsValue;
136
- }
137
- }
138
-
139
- // dashboard widget
140
- if(isset($_POST['optionDW'])){
141
- $showDW = intval($_POST['optionDW']);
142
- }
143
-
144
- // save options
145
- if(!$hasErrors)
146
- {
147
- $opt->showDW = (empty($showDW) ? 0 : 1);
148
- $opt->cleanupRan = 0;
149
- WPPH::updatePluginSettings($opt,null,null,true);
150
- $validationMessage['success'] = __('Your settings have been saved.',WPPH_PLUGIN_TEXT_DOMAIN);
151
- }
152
- }
153
- // end $post
154
- ?>
155
- <style type="text/css">
156
- .widefat td p { margin: 13px 0 0.8em !important; }
157
- .message {
158
- background-color: #FFFFE0 !important;
159
- border-radius: 3px 3px 3px 3px;
160
- border-style: solid;
161
- border-width: 1px;
162
- border-color: #E6DB55;
163
- margin: 5px 0 15px !important;
164
- padding: 0 0.6em !important;
165
- }
166
- .message p { margin: 0 0; padding: 7px 0; font-style: italic; }
167
- p.description span { text-decoration: underline;}
168
- .the-list th, .the-list tr td {padding: 7px 0 0 0 !important;}
169
- .the-list th p, .the-list tr td p {padding: 0 0 !important; margin: 0 0 !important;}
170
- .the-list td.column-username p,
171
- .the-list td.column-name p,
172
- .the-list td.column-role p { padding-left: 7px !important;}
173
- .form-table td { vertical-align: top !important; }
174
- .section-left label { margin-top: 3px !important; display: block;}
175
- </style>
176
- <div id="wpph-pageWrapper" class="wrap">
177
- <h2 class="pageTitle pageTitle-settings"><?php echo __('WP Security Audit Log Settings',WPPH_PLUGIN_TEXT_DOMAIN);?></h2>
178
-
179
- <?php if(! empty($validationMessage)) : ?>
180
- <?php
181
- if(!empty($validationMessage['error'])){ wpph_adminNotice($validationMessage['error']); }
182
- else { wpph_adminUpdate($validationMessage['success']); }
183
- ?>
184
- <?php else : ?>
185
- <div id="errMessage" style="display: none;"></div>
186
- <?php endif;?>
187
- <div style="margin: 20px 0;">
188
- <form id="updateSettingsForm" method="post">
189
- <?php wp_nonce_field('wpph_update_settings','wpph_update_settings_field_nonce'); ?>
190
- <div id="eventsDeletion">
191
- <div id="section-holder">
192
-
193
- <table cellspacing="0" cellpadding="0" class="form-table">
194
- <tbody>
195
- <tr valign="top">
196
- <td rowspan="3" class="section-left">
197
- <label style="display:block;margin: 0 0;" for="eventsNumberInput"><?php echo __('Security Alerts Pruning',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
198
- </td>
199
- </tr>
200
- <tr>
201
- <td class="section-right">
202
- <p>
203
- <input type="radio" id="option1" class="radioInput" style="margin-top: 2px;"/>
204
- <label for="option1"><?php echo __('Delete alerts older than',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
205
- <input type="text" id="daysInput" maxlength="3"
206
- placeholder="<?php echo __('(1 to 365)',WPPH_PLUGIN_TEXT_DOMAIN);?>"
207
- value="<?php if(! empty($daysInput)) { echo $daysInput; } ;?>"/>
208
- <span> <?php echo __('(1 to 365 days)',WPPH_PLUGIN_TEXT_DOMAIN);?></span>
209
- </p>
210
- </td>
211
- </tr>
212
- <tr>
213
- <td class="section-right">
214
- <p>
215
- <?php $wpph_t1 = sprintf(__('(1 to %d alerts)',WPPH_PLUGIN_TEXT_DOMAIN),WPPH_KEEP_MAX_EVENTS); ?>
216
- <input type="radio" id="option2" class="radioInput" style="margin-top: 2px;"/>
217
- <label for="option2"><?php echo __('Keep up to',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
218
- <input type="text" id="eventsNumberInput" maxlength="6"
219
- placeholder="<?php echo $wpph_t1;?>"
220
- value="<?php if(! empty($eventsNumber)) { echo $eventsNumber; } ;?>"/>
221
- <span><?php echo $wpph_t1;?></span>
222
- </p>
223
- <p class="description" style="margin-top: 5px !important;"><?php echo sprintf(__('By default %s will keep up to %d WordPress Security Events.',WPPH_PLUGIN_TEXT_DOMAIN),WPPH_PLUGIN_NAME, WPPH_KEEP_MAX_EVENTS);?></p>
224
- </td>
225
- </tr>
226
- <tr>
227
- <td rowspan="2" class="section-left" style=""><label style="display:block;margin: 0 0;" for="optionDW_on"><?php echo __('Alerts Dashboard Widget',WPPH_PLUGIN_TEXT_DOMAIN);?></label></td>
228
- </tr>
229
- <tr>
230
- <td class="section-right">
231
- <input type="radio" id="optionDW_on" class="radioInput" style="margin-top: 2px;"/><label for="optionDW_on" style="padding-top: 5px; padding-left: 3px;"><?php echo __('On',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
232
- <br/>
233
- <input type="radio" id="optionDW_off" class="radioInput" style="margin-top: 2px;"/><label for="optionDW_off" style="padding-top: 5px; padding-left: 3px;"><?php echo __('Off',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
234
- <br/>
235
- <p class="description" style="margin-top: 5px !important;"><?php echo sprintf(__('Display a dashboard widget with the latest 5 security alerts',WPPH_PLUGIN_TEXT_DOMAIN),WPPH_PLUGIN_NAME, WPPH_KEEP_MAX_EVENTS);?></p>
236
- </td>
237
- </tr>
238
- </tbody>
239
- </table>
240
-
241
- <style type="text/css">
242
- .divTarget { padding: 10px 0;}
243
- .tagElement { padding: 2px 4px; margin: 2px 0 0 2px; border: solid 1px #E6DB55; background: #FFFFE0; }
244
- .tagElement .tagDelete { cursor: pointer; font-weight: 900; }
245
- .tagElement .tagDelete:hover { color: #d00000; }
246
- #section-holder #c-list p.description { margin-top: 0 !important; margin-bottom: 0 !important; }
247
- </style>
248
- <table cellspacing="0" cellpadding="0" class="form-table">
249
- <tbody>
250
- <tr valign="top">
251
- <td valign="top" class="section-left">
252
- <label style="display:block;margin: 0 0;" for="inputUser1"><?php echo __('Can view Security Alerts',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
253
- </td>
254
- <td class="section-right">
255
- <div id="a-list">
256
- <input type="text" id="inputUser1" style="float: left; display: block; width: 250px;"/>
257
- <input type="button" id="inputAdd1" style="float: left; display: block;" class="button-primary" value="<?php echo __('Add',WPPH_PLUGIN_TEXT_DOMAIN);?>"/>
258
- <p class="description" style="clear:both; padding-top: 3px;"><?php echo __('Users and Roles in this list can view the security alerts via the Audit Log Viewer (Read Only)',WPPH_PLUGIN_TEXT_DOMAIN);?></p>
259
- <div id="accessListTarget" class="divTarget" style="float: none; clear: both;"></div>
260
- </div>
261
- </td>
262
- </tr>
263
- <tr valign="top" style="margin-top: 30px;">
264
- <td valign="top" class="section-left">
265
- <label style="display:block;margin: 0 0;" for="inputUser2"><?php echo __('Can Manage Plugin ',WPPH_PLUGIN_TEXT_DOMAIN);?></label>
266
- </td>
267
- <td class="section-right">
268
- <div id="c-list">
269
- <input type="text" id="inputUser2" style="float: left; display: block; width: 250px;"/>
270
- <input type="button" id="inputAdd2" style="float: left; display: block;" class="button-primary" value="<?php echo __('Add',WPPH_PLUGIN_TEXT_DOMAIN);?>"/>
271
- <p class="description" style="clear:both; padding-top: 3px;"><?php echo __('Users and Roles in this list can manage the plugin settings.',WPPH_PLUGIN_TEXT_DOMAIN);?></p>
272
- <div id="changeListTarget" class="divTarget" style="float: none; clear: both;"></div>
273
- </div>
274
- </td>
275
- </tr>
276
- </tbody>
277
- </table>
278
- <script type="text/javascript">
279
- jQuery(document).ready(function($)
280
- {
281
- var mainContainer1 = $('#a-list'),
282
- inputUser1 = $('#inputUser1'),
283
- inputAdd1 = $('#inputAdd1'),
284
- divTarget1 = $('#accessListTarget'),
285
- tagElement1 = $('.tagElement', mainContainer1),
286
- mainContainer2 = $('#c-list'),
287
- inputUser2 = $('#inputUser2'),
288
- inputAdd2 = $('#inputAdd2'),
289
- divTarget2 = $('#changeListTarget'),
290
- tagElement2 = $('.tagElement', mainContainer2)
291
- ;
292
-
293
- function showDefaultEntries(data, divTarget){
294
- if(data.length > 0){
295
- $.each(data, function(i,v){
296
- createTag($, v, divTarget);
297
- });
298
- }
299
- }
300
-
301
- // Display the default list
302
- <?php
303
- if(!empty($allowedAccess)){ echo 'showDefaultEntries(["'.implode('","',$allowedAccess).'"],divTarget1);'; }
304
- if(!empty($allowedChange)){ echo 'showDefaultEntries(["'.implode('","',$allowedChange).'"],divTarget2);'; }
305
- ?>
306
-
307
- function _ajax(string)
308
- {
309
- if(string.length > 0){
310
- var data = {
311
- 'action': 'wpph_check_user_role',
312
- 'check_input' : string
313
- };
314
- }
315
- else {
316
- alert('Please add a user name or role.');
317
- return false;
318
- }
319
- var result;
320
- $.ajax({
321
- url: ajaxurl,
322
- cache: false,
323
- type: 'POST',
324
- data: data,
325
- async : false,
326
- success: function(response) {
327
- result = response;
328
- },
329
- error: function() {
330
- result = 'An error occurred. Please try again in a few moments.';
331
- }
332
- });
333
- return result;
334
- }
335
- function createTag($, value, parentElement){ parentElement.append($('<span class="tagElement"><span class="tagName">'+value+'</span> <span class="tagDelete" title="Delete">x</span></span>')); }
336
- function isValidEntry($, value, targetDiv){
337
- var elements = $('.tagName', targetDiv);
338
- var result = true;
339
- if(elements.length > 0){
340
- $.each(elements,function(i,v){
341
- var val = $.trim($(this).text());
342
- if($.trim(value) == val){
343
- result = false;
344
- return false;
345
- }
346
- });
347
- }
348
- return result;
349
- }
350
- $('.tagDelete', tagElement1).live('click', function(){ $(this).parent().remove(); });
351
- $('.tagDelete', tagElement2).live('click', function(){ $(this).parent().remove(); });
352
- inputAdd1.on('click', function(){
353
- var val = $.trim(inputUser1.val());
354
- if(val.length == 0){
355
- alert('Please add a user name or role.');
356
- return false;
357
- }
358
- if( false == isValidEntry($, val, divTarget1)){
359
- alert('The user or role has been already added.');
360
- return false;
361
- }
362
- else {
363
- var result = _ajax(val);
364
- if(result.length > 5){
365
- alert('Error: '+result);
366
- return false;
367
- }
368
- else if (parseInt(result) == 0){
369
- alert('user or role '+val+' was not found');
370
- return false;
371
- }
372
- createTag($, val, divTarget1);
373
- inputUser1.val('');
374
- return true;
375
- }
376
- });
377
- inputUser1.keypress(function(event){
378
- if (event.keyCode == 10 || event.keyCode == 13) {
379
- event.preventDefault();
380
- inputAdd1.click();
381
- }
382
- });
383
- inputAdd2.on('click', function(){
384
- var val = $.trim(inputUser2.val());
385
- if(val.length == 0){
386
- alert('Please add a user name or role.');
387
- return false;
388
- }
389
- if( false == isValidEntry($, val, divTarget2)){
390
- alert('The user or role has been already added.');
391
- return false;
392
- }
393
- else {
394
- var result = _ajax(val);
395
- if(result.length > 5){
396
- alert('Error: '+result);
397
- return false;
398
- }
399
- else if (parseInt(result) == 0){
400
- alert('user or role '+val+' was not found');
401
- return false;
402
- }
403
- createTag($, val, divTarget2);
404
- inputUser2.val('');
405
- return true;
406
- }
407
- });
408
- inputUser2.keypress(function(event){
409
- if (event.keyCode == 10 || event.keyCode == 13) {
410
- event.preventDefault();
411
- inputAdd2.click();
412
- }
413
- });
414
- });
415
- </script>
416
- </div>
417
- </div>
418
- <p style="margin-top: 40px;">
419
- <input type="submit" id="submitButton" class="button button-primary" value="<?php echo __('Save settings',WPPH_PLUGIN_TEXT_DOMAIN);?>"/>
420
- </p>
421
- <input type="hidden" id="deleteEventsBy" name="deleteEventsBy" value=""/>
422
- <input type="hidden" id="deleteEventsValue" name="deleteEventsValue" value=""/>
423
- <input type="hidden" id="optionDW" name="optionDW" value=""/>
424
- <input type="hidden" id="accessListInput" name="accessListInput" value=""/>
425
- <input type="hidden" id="changeListInput" name="changeListInput" value=""/>
426
- </form>
427
- </div>
428
- </div>
429
- <br class="clear"/>
430
-
431
- <script type="text/javascript">
432
- jQuery(document).ready(function($){
433
- var showErrorMessage = function(msg){
434
- $('#errMessage').removeClass('updated').addClass('error').html("<p>Error: "+msg+"</p>").show();
435
- };
436
- var setFocusOn = function($e){
437
- $e.focus();
438
- $e.select();
439
- };
440
- var validateDeleteOptions = function(section, $daysInput, $eventsNumberInput)
441
- {
442
- if(section == 0){
443
- showErrorMessage("<?php echo __('Invalid form. Please reload the page and try again.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
444
- setFocusOn($daysInput);
445
- return false;
446
- }
447
- // validate fields
448
- if(section == 1)
449
- {
450
- var daysInputVal = $daysInput.val();
451
-
452
- if(daysInputVal.length == 0){
453
- showErrorMessage("<?php echo __('Please input the number of days.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
454
- setFocusOn($daysInput);
455
- return false;
456
- }
457
- if(daysInputVal == 0){
458
- showErrorMessage("<?php echo __('Please input a number greater than 0.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
459
- setFocusOn($daysInput);
460
- return false;
461
- }
462
- if(!/^\d+$/.test(daysInputVal)){
463
- showErrorMessage("<?php echo __('Only numbers greater than 0 allowed.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
464
- setFocusOn($daysInput);
465
- return false;
466
- }
467
- if(daysInputVal > 365){
468
- showErrorMessage("<?php echo __('Incorrect number of days. Please specify a value between 1 and 365.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
469
- setFocusOn($daysInput);
470
- return false;
471
- }
472
- }
473
- else if(section == 2)
474
- {
475
- var eniVal = $eventsNumberInput.val();
476
-
477
- if(eniVal.length == 0){
478
- showErrorMessage("<?php echo __('Please input the number of alerts.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
479
- setFocusOn($eventsNumberInput);
480
- return false;
481
- }
482
- if(eniVal == 0){
483
- showErrorMessage("<?php echo __('Please input a number greater than 0.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
484
- setFocusOn($eventsNumberInput);
485
- return false;
486
- }
487
- if(!/^\d+$/.test(eniVal)){
488
- showErrorMessage("<?php echo __('Only numbers greater than 0 allowed.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
489
- setFocusOn($eventsNumberInput);
490
- return false;
491
- }
492
- if(eniVal > <?php echo WPPH_KEEP_MAX_EVENTS;?>){
493
- showErrorMessage("<?php echo sprintf(__('Incorrect number of security alerts. Please specify a value between 1 and %d.',WPPH_PLUGIN_TEXT_DOMAIN),WPPH_KEEP_MAX_EVENTS);?>");
494
- setFocusOn($eventsNumberInput);
495
- return false;
496
- }
497
- }
498
- return true;
499
- };
500
-
501
- var deb = $('#deleteEventsBy')
502
- ,debv = $('#deleteEventsValue')
503
- ,option1 = $('#option1')
504
- ,option2 = $('#option2')
505
- ,daysInput = $('#daysInput')
506
- ,eventsNumber = $('#eventsNumberInput')
507
- ,showDW = $('#optionDW_on')
508
- ,hideDW = $('#optionDW_off')
509
- ,accessListInput = $('#accessListInput')
510
- ,changeListInput = $('#changeListInput');
511
- option1.on('click', function(){ option2.removeAttr('checked'); $(this).attr('checked','checked'); setFocusOn(daysInput); });
512
- option2.on('click', function(){ option1.removeAttr('checked'); $(this).attr('checked','checked'); setFocusOn(eventsNumber); });
513
- daysInput.on('click', function(){ option2.removeAttr('checked'); option1.attr('checked','checked'); });
514
- eventsNumber.on('click', function(){ option1.removeAttr('checked'); option2.attr('checked','checked'); });
515
- showDW.on('click', function(){ hideDW.removeAttr('checked'); $(this).attr('checked','checked'); setFocusOn($(this)); });
516
- hideDW.on('click', function(){ showDW.removeAttr('checked'); $(this).attr('checked','checked'); setFocusOn($(this)); });
517
-
518
- // select delete option
519
- <?php if($activeOption == 1):?>
520
- option1.attr('checked','checked');
521
- eventsNumber.val("");
522
- <?php else :?>
523
- option2.attr('checked','checked');
524
- daysInput.val("");
525
- <?php endif; ?>
526
-
527
- //select DW
528
- <?php if($showDW):?>
529
- showDW.attr('checked','checked');
530
- <?php else :?>
531
- hideDW.attr('checked','checked');
532
- <?php endif;?>
533
-
534
- // form submit
535
- $('#submitButton').on('click',function()
536
- {
537
- var section = 0;
538
- if ($('#option1').prop('checked')){section = 1;}
539
- else { section = 2; }
540
-
541
- if(section < 1){
542
- alert("<?php echo __('Invalid form. Please refresh the page and try again.',WPPH_PLUGIN_TEXT_DOMAIN);?>");
543
- return false;
544
- }
545
- if(! validateDeleteOptions(section, daysInput, eventsNumber)){
546
- return false;
547
- }
548
- // alerts pruning
549
- if(section == 1){
550
- deb.val(1);
551
- debv.val(daysInput.val());
552
- }
553
- else if(section ==2){
554
- deb.val(2);
555
- debv.val(eventsNumber.val());
556
- }
557
- // dashboard widget
558
- if(showDW.prop('checked')){
559
- $('#optionDW').val('1');
560
- }
561
- else { $('#optionDW').val('0') }
562
-
563
- //#! build the access list
564
- var a = [];
565
- $('.tagName', $('#a-list')).each(function(){a.push($(this).text());});
566
- accessListInput.val(JSON.stringify(a));
567
-
568
- // build the change list
569
- var b = [];
570
- $('.tagName', $('#c-list')).each(function(){b.push($(this).text());});
571
- changeListInput.val(JSON.stringify(b));
572
-
573
- return true;
574
- });
575
- });
576
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
pages/support.php DELETED
@@ -1,22 +0,0 @@
1
- <?php if(! WPPHUtil::canViewPage()){ return; } ?><?php
2
- if(! WPPH::ready())
3
- {
4
- $errors = WPPH::getPluginErrors();
5
- foreach($errors as $error) {
6
- wpph_adminNotice($error);
7
- }
8
- echo '<div id="wpph-pageWrapper" class="wrap">';
9
- echo '<p>'.__('We have encountered some errors during the installation of the plugin which you can find above.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
10
- echo '<p>'.__('Please try to correct them and then reactivate the plugin.',WPPH_PLUGIN_TEXT_DOMAIN).'</p>';
11
- echo '</div>';
12
- return;
13
- }
14
- ?>
15
- <div id="wpph-pageWrapper" class="wrap">
16
- <h2 class="pageTitle pageTitle-support"><?php echo __('Support',WPPH_PLUGIN_TEXT_DOMAIN);?></h2>
17
- <div>
18
- <p><?php echo
19
- sprintf(__('Thank you for showing interest and using our plugin. If you encounter any issues running this plugin, or have suggestions or queries, please get in touch with us on %s.',WPPH_PLUGIN_TEXT_DOMAIN),
20
- '<a href="mailto:plugins@wpwhitesecurity.com">plugins@wpwhitesecurity.com</a>');?></p>
21
- </div>
22
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,57 +1,65 @@
1
  === WP Security Audit Log ===
2
- Contributors: WPWhiteSecurity
3
  License: GPLv3
4
  License URI: http://www.gnu.org/licenses/gpl.html
5
- Tags: wordpress security plugin, wordpress security audit log, audit log, event log wordpress, wordpress user tracking, wordpress activity log, wordpress audit, security event log, audit trail, security audit trail, wordpress security alerts, wordpress monitor, wordpress security monitor, wordpress admin, wordpress admin monitoring, wordpress security monitor, analytics, activity, admin, multisite, wordpress multisite
6
- Requires at least: 3.0
7
  Tested up to: 3.9.1
8
- Stable tag: 0.6.3
9
 
10
  Identify WordPress issues before they become a security problem by keeping an audit log of users and all of the under the hood WordPress activity.
11
 
12
  == Description ==
13
- Identify WordPress security issues before they become a security problem by keeping a security audit log of what is happening under the hood of your WordPress blog or website or your WordPress Multisite installation. WP Security Audit Log plugin is developed by WordPress Security Consultants and Professionals [WP White Security](http://www.wpwhitesecurity.com/wordpress-security-services/) and works on both WordPress and [WordPress Multisite](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/).
14
 
15
  = Keep A WordPress Security Audit Log & Identify WordPress Security Issues =
16
  WP Security Audit Log keeps a log of 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 security alert is generated by the plugin when:
17
 
18
- * New user is created via registration or created by another user
19
- * Existing user changes the role or password of another user
20
- * Existing user on a WordPress multisite network is added to a site
21
  * User uploads or deletes a file, changes a password or email address
22
  * User installs, activates, deactivates, upgrades or uninstalls a plugin
23
  * User creates a new post, page, category or a custom post type
24
  * User modifies an existing post, page, category or a custom post type
25
- * User adds, modifies or deletes a widget
26
- * WordPress settings are changed
 
 
27
  * Failed login attempts
28
  * and much more...
29
 
30
- Refer to the complete list of [WordPress Security Audit Alerts](http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/security-audit-alerts-logs/) for more information.
31
 
32
  = Monitor WordPress Users Activity & Productivity =
33
- If you own a multi user WordPress blog or website, or a WordPress multisite network installation you can use the WP Security Audit Log plugin to monitor your users' activity and productivity. With WP Security Audit Log WordPress plugin you can monitor:
34
 
35
- * When WordPress users logged in or out
36
  * From where WordPress users are logging in
37
- * Users who created or deleted categories
38
- * Users who created a blog post or a page
39
- * Users who published a blog post or a page
40
- * Users who modified published WordPress content such as a page or a blog post
 
41
  * Users who modify WordPress widgets
42
- * Uses who upload or delete files
43
  * and much more...
44
 
45
- Refer to the complete list of [WordPress Security Audit Alerts](http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/security-audit-alerts-logs/) for more information.
46
 
47
  = WP Security Audit Log for WordPress Multisite =
48
- WP Security Audit Log is the first and only tracking and audit security plugin that supports WordPress multisite network installations.
49
 
50
- For more information about WP Security Audit log plugin features for WordPress Multisite network installation refer to [WP Security Audit Log Features for WordPress Multisite](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/)
51
 
52
  = WordPress Security Audit Log in your Language! =
53
  We need help translating the plugin and the WordPress Security Events. If you're good at translating, please drop us an email on plugins@wpwhitesecurity.com.
54
 
 
 
 
 
 
55
  = WordPress Security Tips & Tricks =
56
  Even if WordPress security is not your cup of tea, the security of your WordPress is your responsibility. Keep yourself up to date with the latest WordPress Security Tips & Tricks. WP White Security frequently publishes WordPress security tips & tricks on the [WordPress Security section](http://www.wpwhitesecurity.com/wordpress-security/) of their blog.
57
 
@@ -82,10 +90,10 @@ Yes. A complete list can be found [here](http://www.wpwhitesecurity.com/wordpres
82
 
83
  = Can I disable some WordPress security alerts? =
84
 
85
- Yes it is possible to disable (and re-enable later) specific WordPress security alerts. To do so navigate to the "Enable/Disable Alerts" node in the plugin, select the category tab and untick the WordPress security alert. Tick back the alert to re-enable it.
86
 
87
  = Can WP Security Audit Log plugin work and monitor activity on WodPress Multisite? =
88
- Yes, WP Security Audit Log works on WordPress Multisite networkds. For more information refer to the post [WP Security Audit Log Features for WordPress Multisite installation](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/).
89
 
90
  == Screenshots ==
91
 
@@ -96,6 +104,39 @@ Yes, WP Security Audit Log works on WordPress Multisite networkds. For more info
96
 
97
  == Changelog ==
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  = 0.6.3 (2014-02-18) =
100
  * Bug Fix
101
  * Disabled debugging by default (left enabled by mistake)
@@ -111,7 +152,7 @@ Yes, WP Security Audit Log works on WordPress Multisite networkds. For more info
111
 
112
  = 0.6 (2014-01-15) =
113
  * New Plugin Feature
114
- * WordPress Multisite Support [Read More](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/)
115
 
116
  * New WordPress Security Alerts for monitoring specific multisite activity on a WordPress multisite network installation
117
  * Alert 4008: User is granted super admin privileges (network)
1
  === WP Security Audit Log ===
2
+ Contributors: WPWhiteSecurity, uuf6429
3
  License: GPLv3
4
  License URI: http://www.gnu.org/licenses/gpl.html
5
+ Tags: wordpress security plugin, wordpress security audit log, audit log, event log wordpress, wordpress user tracking, wordpress activity log, wordpress audit, security event log, audit trail, security audit trail, wordpress security alerts, wordpress monitor, wordpress security monitor, wordpress admin, wordpress admin monitoring, analytics, activity, admin, multisite, wordpress multisite
6
+ Requires at least: 3.6
7
  Tested up to: 3.9.1
8
+ Stable tag: 1.0.0
9
 
10
  Identify WordPress issues before they become a security problem by keeping an audit log of users and all of the under the hood WordPress activity.
11
 
12
  == Description ==
13
+ Identify WordPress security issues before they become a security problem by keeping a security audit log of what is happening under the hood of your WordPress blog or website or your WordPress Multisite installation. WP Security Audit Log plugin is developed by WordPress Security Consultants and Professionals [WP White Security](http://www.wpwhitesecurity.com/wordpress-security-services/) and is the only WordPress monitoring and auditing plugin that works on both WordPress single site installations and [WordPress Multisite](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/).
14
 
15
  = Keep A WordPress Security Audit Log & Identify WordPress Security Issues =
16
  WP Security Audit Log keeps a log of 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 security alert is generated by the plugin when:
17
 
18
+ * New user is created via registration or by another user
19
+ * User changes the role, password or other profile settings of another user
20
+ * User on a WordPress multisite network is added or removed from a site
21
  * User uploads or deletes a file, changes a password or email address
22
  * User installs, activates, deactivates, upgrades or uninstalls a plugin
23
  * User creates a new post, page, category or a custom post type
24
  * User modifies an existing post, page, category or a custom post type
25
+ * User adds, moves, modifies or deletes a widget
26
+ * User installs or activates a new WordPress theme
27
+ * User changes WordPress settings such as permalinks or administrator notification email
28
+ * WordPress is updated / upgraded
29
  * Failed login attempts
30
  * and much more...
31
 
32
+ Refer to the complete list of [WordPress Security Audit Alerts](http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/security-audit-alerts-logs/) for more information on what WordPress activity can be monitored with WP Security Audit Log.
33
 
34
  = Monitor WordPress Users Activity & Productivity =
35
+ If you own a multi user WordPress blog or website, or a WordPress multisite network installation you can use WP Security Audit Log plugin to monitor your users' activity and productivity. With WP Security Audit Log WordPress plugin you can monitor:
36
 
37
+ * When WordPress users log in or out
38
  * From where WordPress users are logging in
39
+ * Users who created. modified or deleted categories
40
+ * Users who created a blog post, page or a custom post
41
+ * Users who published a blog post, page or a custom post
42
+ * Users who modified published WordPress content such as custom posts, pages or a blog posts
43
+ * Users who moves content such as blog posts or WordPress pages to trash or permanently deletes it
44
  * Users who modify WordPress widgets
45
+ * Uses who upload or delete any sort of files
46
  * and much more...
47
 
48
+ Refer to the complete list of [WordPress Security Audit Alerts](http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/security-audit-alerts-logs/) for more information on what other WordPress user activity can be monitored with the WP Security Audit Log WordPress plugin.
49
 
50
  = WP Security Audit Log for WordPress Multisite =
51
+ WP Security Audit Log is the first tracking and audit WordPress security monitoring plugin that supports WordPress multisite network installations and can monitor activity on such WordPress multisite network installations.
52
 
53
+ For more information about the features for WordPress Multisite network installation refer to [WP Security Audit Log Features for WordPress Multisite](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/)
54
 
55
  = WordPress Security Audit Log in your Language! =
56
  We need help translating the plugin and the WordPress Security Events. If you're good at translating, please drop us an email on plugins@wpwhitesecurity.com.
57
 
58
+ = WordPress & PHP Errors Monitoring Tools =
59
+ Plugins and themes customizations are most probably the norm of the day on large WordPress websites, not to mention the installation of new plugins and components. Unforunately sometimes such changes create problems and it is not always easy and possible to setup real live scenarios and replicate specific problems or bugs.
60
+
61
+ With WP Security Audit Log now it is easier than ever before to monitor your plugins', theme's and other code behaviour, it will generate a alert when a PHP error, warning, exception or shutdown is detected. It is also possible to log all HTTP GET and POST requests that are reaching your WordPress installation to a log file with WP Security Audit Log. Simply enable the PHP Errors monitoring or logging from the plugins settings.
62
+
63
  = WordPress Security Tips & Tricks =
64
  Even if WordPress security is not your cup of tea, the security of your WordPress is your responsibility. Keep yourself up to date with the latest WordPress Security Tips & Tricks. WP White Security frequently publishes WordPress security tips & tricks on the [WordPress Security section](http://www.wpwhitesecurity.com/wordpress-security/) of their blog.
65
 
90
 
91
  = Can I disable some WordPress security alerts? =
92
 
93
+ Yes it is possible to disable (and re-enable) specific WordPress security alerts. To do so navigate to the "Enable/Disable Alerts" node in the plugin's menu, select the category tab and untick the WordPress security alert. Tick back the alert to re-enable it.
94
 
95
  = Can WP Security Audit Log plugin work and monitor activity on WodPress Multisite? =
96
+ Yes, WP Security Audit Log works on WordPress Multisite networks, i.e. it can monitor user and under the hood WordPress activity on WordPress multisite installations. For more information refer to the post [WP Security Audit Log Features for WordPress Multisite installation](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/).
97
 
98
  == Screenshots ==
99
 
104
 
105
  == Changelog ==
106
 
107
+ = 1.0 (2014-05-20) =
108
+ * Complete plugin rewrite making the new version more stable and scalable
109
+
110
+ * New Features
111
+ * New Audit Log viewer
112
+ * Auto refresh of security alerts - WordPress administrators do not need to refresh the Audit Log Viewer page to see new alerts
113
+ * Data Inspector reports more insider information about each alert (can be enabled from settings)
114
+ * Sandbox allows developers to execute PHP code for troubleshooting (can be enabled from settings)
115
+ * Request Log that logs all HTTP GET and POST requests done on WordPress (can be enabled from settings)
116
+ * Logging of PHP Errors; ideal for developers who want to monitor WordPress for any errors (can be enabled from settings)
117
+ * New Support and About Us page that you should check out!
118
+
119
+ * New WordPress Security Alerts for monitoring themes, WordPress settings, files and much more...
120
+ * Alert 2046: User modified a file using the editor
121
+ * Alert 2047: User changed parent of page
122
+ * Alert 2048: User changed template of page
123
+ * Alert 2049: User set post as sticky
124
+ * Alert 2050: User removed post from Sticky
125
+ * Alert 5005: User installed a new theme
126
+ * Alert 5006: User activated a theme
127
+ * Alert 6004: User upgraded WordPress
128
+ * Alert 6005: User changed the WordPress permalinks
129
+
130
+ * New WordPress Developer Alerts
131
+ * Alert 0000: Unknown error
132
+ * Alert 0001: PHP Error
133
+ * Alert 0002: PHP Warning
134
+ * Alert 0003: PHP Notice
135
+ * Alert 0004: PHP Exception
136
+ * Alert 0005: PHP Shutdown Error
137
+
138
+ * For more information about what is new and changed in this version of the plugin refer to the [WP Security Audit Log release notes](http://www.wpwhitesecurity.com/wordpress-plugins/easy-wordpress-security-monitor-wp-security-audit-log-plugin).
139
+
140
  = 0.6.3 (2014-02-18) =
141
  * Bug Fix
142
  * Disabled debugging by default (left enabled by mistake)
152
 
153
  = 0.6 (2014-01-15) =
154
  * New Plugin Feature
155
+ * WordPress Multisite Support [Read More](http://www.wpwhitesecurity.com/wordpress-plugins/wp-security-audit-log-plugin-features-wordpress-multisite/)
156
 
157
  * New WordPress Security Alerts for monitoring specific multisite activity on a WordPress multisite network installation
158
  * Alert 4008: User is granted super admin privileges (network)
res/css/styles.base.css DELETED
@@ -1,122 +0,0 @@
1
- #wpph-pageWrapper{ padding: 0 0; clear: both; }
2
-
3
- h2.pageTitle { padding-left: 40px; }
4
- h2.pageTitle-eventViewer { background: url("../img/page-viewer-logo.png") no-repeat left center; }
5
- h2.pageTitle-settings { background: url("../img/page-settings-logo.png") no-repeat left center; }
6
- h2.pageTitle-support { background: url("../img/page-support-logo.png") no-repeat left center; }
7
- h2.pageTitle-about { background: url("../img/page-about-logo.png") no-repeat left center; }
8
-
9
- #wpph-pageWrapper .button { margin-top: 1px;}
10
- #wpph-pageWrapper .buttonRefreshEventsList { margin-top: 3px;}
11
-
12
- .wpph-text-disabled { color: #c0c0c0 !important; }
13
-
14
-
15
- /*
16
- * Page: Event Viewer
17
- */
18
- .column-left-align { text-align: left; }
19
- .column-center-align { text-align: center; }
20
-
21
- #the-list .row-0 { background-color: #FCFCFC; }
22
- .widefat tbody th.check-column { padding: 8px 0 0 0 !important; }
23
- .widefat .check-column { padding: 6px 0 0 0; }
24
-
25
- #the-list th, #the-list tr, #the-list td { padding: 0 0 0 0 !important; line-height: normal !important;}
26
- #the-list span { padding: 5px 5px; line-height: normal !important; display: block; }
27
-
28
- /*
29
- * Page: Settings, Alerts
30
- */
31
- .error-info-icon {
32
- padding: 5px 7px 5px 20px;
33
- margin-left: 8px;
34
- color: #ff0000;
35
- background: url("../img/error-icon.png") no-repeat left center;
36
- }
37
- .success-info-icon {
38
- padding: 5px 7px 5px 20px !important;
39
- margin-left: 8px;
40
- color: #000000;
41
- background: url("../img/success-icon.png") no-repeat left center;
42
- }
43
-
44
- #section-holder { overflow: hidden; }
45
- #section-holder p { margin: 15px 0; overflow: hidden; }
46
- #section-holder p input[type="radio"] { float: left; margin-top: 5px !important; }
47
- #section-holder p label, #section-holder p span { float: left; margin-top: 4px !important; margin-left: 10px !important; font-size: 12px !important; }
48
- #section-holder p input[type="text"] { float: left; margin-left: 10px !important; }
49
-
50
- .form-table, .form-table td { border: none 0;}
51
- .form-table td { line-height: normal !important; }
52
- .form-table td.section-left { width: 190px; padding: 0 0; }
53
- .form-table td.section-right { padding: 0 0 20px 0 !important; margin: 0 0 !important; }
54
- .form-table td.section-right p { padding: 0 0 !important; margin: 0 0 !important; }
55
- .form-table td.section-left label { font-weight: bold; }
56
-
57
- #errMessage { margin: 20px 0; }
58
- #eventsDeletion #description,
59
- #optionsDescription #description {
60
- background: none repeat scroll 0 0 #EEEEEE;
61
- margin: 0 0 20px 0;
62
- padding: 10px;
63
- }
64
- #optionsDescription #description { margin: 20px 0; }
65
-
66
- .widefat .item-cb_h { width: 50px !important; }
67
- .widefat .item-event { width: 100px !important; text-align: left; padding: 8px 7px 2px 7px !important; }
68
- .widefat .item-type { min-width: 100px !important; text-align: left; padding: 8px 7px 2px 7px !important; }
69
- .widefat .item-description { width: auto !important; text-align: left; padding: 8px 7px 2px 7px !important; }
70
- .widefat tbody th.check-column { padding: 11px 0 0 3px !important; }
71
- /*
72
- * Page: Events :: pagination
73
- */
74
- .paginationWrapper{
75
- overflow: hidden;
76
- float: right;
77
- display: block;
78
- margin-right: 0;
79
- }
80
- .paginationWrapper .showPages {
81
- float: left;
82
- display: block;
83
- margin-top:10px;
84
- margin-right: 7px;
85
- }
86
- .paginationWrapper .showPages .span1,
87
- .paginationWrapper .showPages .span2,
88
- .paginationWrapper .showPages .span3 { font-weight: 800; }
89
-
90
- .paginationWrapper .pageButton {
91
- color: #B8B8B8;
92
- background-color: transparent;
93
- background-image: -moz-linear-gradient(center top , #F5F5F5, #F1F1F1);
94
- border: 1px solid rgba(0, 0, 0, 0.1);
95
- cursor: default;
96
- outline: 0;
97
- text-align: center;
98
- white-space: nowrap;
99
- display: inline-block;
100
- padding: 0 0;
101
- margin: 0 0;
102
- }
103
- .pageButton span{
104
- display: block;
105
- font-family: Verdana, "Arial Unicode MS", Arial;
106
- font-weight: 800;
107
- font-size: 18px;
108
- color: #000;
109
- margin-right: 0;
110
- margin-top: -1px;
111
- padding: 2px 7px 4px;
112
- }
113
- .paginationWrapper .buttonsWrapper{
114
- overflow: hidden;
115
- float: right;
116
- display: block;
117
- width: 70px;
118
- }
119
- .buttonsWrapper .buttonPrevious,
120
- .buttonsWrapper .buttonNext {float: right;}
121
- .wpphButtonDisabled { background: #ccc !important; }
122
- .wpphButtonDisabled span { color: #808080 !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
res/img/ajax-loader.gif DELETED
Binary file
res/img/error-icon.png DELETED
Binary file
res/img/page-about-logo.png DELETED
Binary file
res/img/page-settings-logo.png DELETED
Binary file
res/img/page-support-logo.png DELETED
Binary file
res/img/page-viewer-logo.png DELETED
Binary file
res/img/success-icon.png DELETED
Binary file
res/js/audit-view-model.js DELETED
@@ -1,314 +0,0 @@
1
- var AuditLogViewModel = (function($, ko) {
2
- "use strict";
3
- /*global AjaxLoaderShow, AjaxLoaderHide, ajaxurl, __ajaxLoaderTargetElement__*/
4
- /*jshint curly:false,devel:true*/
5
-
6
- var error = ko.observable('');
7
- var loading = ko.observable(false);
8
- var events = ko.observableArray([]);
9
- var totalEventsCount = ko.observable(0);
10
- var offset = ko.observable(0);
11
- var availablePageSize = ko.observableArray([25, 50, 100]);
12
- var isMainSite = ko.observable(false);
13
- var _blogId = ko.observable();
14
- var blogId = ko.computed({
15
- read: function() {
16
- var parsedValue = parseInt(_blogId(), 10);
17
- return isNaN(parsedValue) ? undefined : parsedValue;
18
- },
19
- write: function(newValue) {
20
- newValue = parseInt(newValue, 10);
21
- if (isNaN(newValue)) newValue = undefined;
22
- if (_blogId() !== newValue) {
23
- // console.warn('blogId changed from %o to %o', _blogId(), newValue);
24
- var refresh = false;
25
- if (_blogId() !== undefined) {
26
- refresh = true;
27
- }
28
- _blogId(newValue);
29
- if (refresh) {
30
- refreshEvents(0, newValue);
31
- }
32
- }
33
- }
34
- });
35
- var blogList = ko.observableArray();
36
-
37
- var columns = ko.observableArray([
38
- {columnHeader: 'Event', columnName: 'EventNumber', sortable: true, columnWidth: '5%', sorted: ko.observable(false), sortedDescending: ko.observable(false), visible: true },
39
- {columnHeader: 'ID', columnName: 'EventID', sortable: true, columnWidth: '5%', sorted: ko.observable(false), sortedDescending: ko.observable(false), visible: true},
40
- {columnHeader: 'Date', columnName: 'EventDate', sortable: true, columnWidth: '11%', sorted: ko.observable(false), sortedDescending: ko.observable(false), visible: true},
41
- {columnHeader: 'Type', columnName: 'EventType', sortable: true, columnWidth: '6%', sorted: ko.observable(false), sortedDescending: ko.observable(false), visible: true},
42
- {columnHeader: 'IP Address', columnName: 'UserIP', sortable: true, columnWidth: '9%', sorted: ko.observable(false), sortedDescending: ko.observable(false), visible: true},
43
- {columnHeader: 'User', columnName: 'UserID', sortable: true, columnWidth: '10%', sorted: ko.observable(false), sortedDescending: ko.observable(false), visible: true},
44
- {columnHeader: 'Site', columnName: 'SiteName', sortable: true, columnWidth: '10%', sorted: ko.observable(true), sortedDescending: ko.observable(false), visible: ko.computed(function() {
45
- return blogId() === 0;
46
- })},
47
- {columnHeader: 'Description', columnName: 'EventDescription', sortable: false, columnWidth: 'auto', sorted: ko.observable(false), sortedDescending: ko.observable(false), visible: true}
48
- ]);
49
-
50
-
51
- var _initialPageSize = parseInt($.cookie('wpph_ck_page_size'), 10);
52
- if (availablePageSize.indexOf(_initialPageSize) < 0) {
53
- _initialPageSize = availablePageSize()[1];
54
- }
55
-
56
- var pageSize = ko.observable(_initialPageSize);
57
- var selectedPageSize = ko.observable(_initialPageSize);
58
- var pageCount = ko.computed(function() {
59
- return Math.ceil(totalEventsCount() / pageSize());
60
- });
61
-
62
- var _currentPageIndex = 1;
63
- var currentPage = ko.computed({
64
- read: function() {
65
- return _currentPageIndex;
66
- },
67
- write: function(value) {
68
- value = parseInt(value, 10);
69
- if (isNaN(value) || value < 1 || value > pageCount()) {
70
- return;
71
- }
72
- _currentPageIndex = value;
73
- currentPage.notifySubscribers();
74
- $('#fdr').val(_currentPageIndex);
75
- }
76
- });
77
-
78
- var orderBy = ko.computed({
79
- read: function() {
80
- var columnInfo = ko.utils.arrayFirst(columns(), function(item) { return item.sorted(); });
81
- return columnInfo && columnInfo.columnName || '';
82
- },
83
- write: function(value) {
84
- var columnInfo = ko.utils.arrayFirst(columns(), function(item) {
85
- return item.columnName === value;
86
- });
87
- if (columnInfo) {
88
- ko.utils.arrayForEach(columns(), function(item) {
89
- item.sorted(false);
90
- item.sortedDescending(false);
91
- });
92
- columnInfo.sorted(value);
93
- }
94
- }
95
- });
96
- var orderByDescending = ko.computed({
97
- read: function() {
98
- var columnInfo = ko.utils.arrayFirst(columns(), function(item) { return item.sorted(); });
99
- return !!(columnInfo && columnInfo.sortedDescending());
100
- },
101
- write: function(value) {
102
- var columnInfo = ko.utils.arrayFirst(columns(), function(item) { return item.sorted(); });
103
- if (columnInfo) columnInfo.sortedDescending(value);
104
- }
105
- });
106
-
107
-
108
- function loadRemoteData(newOffset, bid) {
109
- newOffset = newOffset || 0;
110
-
111
- var _blogId = null;
112
-
113
- if(bid !== null){
114
- _blogId = bid;
115
- }
116
- else {
117
- _blogId = parseInt(blogId(), 10);
118
- if (isNaN(_blogId)) _blogId = undefined;
119
- }
120
-
121
- var data = {
122
- action: 'wpph_get_events',
123
- orderBy: orderBy(),
124
- sort: orderByDescending() ? 'desc' : 'asc',
125
- offset: newOffset,
126
- count: pageSize(),
127
- blogID: _blogId
128
- };
129
-
130
- // busy property possible?
131
- AjaxLoaderShow(__ajaxLoaderTargetElement__);
132
- loading(true);
133
-
134
- $.ajax({ url: ajaxurl, cache: false, type: 'POST', data: data, dataType: 'json' })
135
- .then(function (response) {
136
- if (response.dataSource.blogs) {
137
- blogList.removeAll();
138
- ko.utils.arrayPushAll(blogList(), response.dataSource.blogs);
139
- }
140
-
141
- if (response.error.length > 0) {
142
- error(response.error);
143
- events.removeAll();
144
- totalEventsCount(0);
145
- offset(0);
146
- $('#wpph_ew').attr('colspan', _blogId===undefined ? 8 : 7);
147
- return;
148
- }
149
- events(response.dataSource.events);
150
- totalEventsCount(response.dataSource.eventsCount);
151
- offset(newOffset);
152
-
153
- if (response.dataSource.blogID) {
154
- blogId(response.dataSource.blogID);
155
- }
156
-
157
- if (totalEventsCount() < offset()) {
158
- offset(0);
159
- }
160
-
161
- if (offset() === 0) {
162
- currentPage(1);
163
- }
164
- else { currentPage(1 + offset() / pageSize()); }
165
- })
166
- .fail(function () {
167
- //Report data loading error
168
- error("An error occurred while loading data. Please try again in a few moments.");
169
- events.removeAll();
170
- totalEventsCount(0);
171
- offset(0);
172
- })
173
- .always(function () {
174
- loading(false);
175
- AjaxLoaderHide(__ajaxLoaderTargetElement__);
176
- });
177
- }
178
-
179
- function onCurrentPageInputKeyDown(viewModel, event) {
180
- if (event.keyCode === 13) {
181
- var value = parseInt(event.currentTarget.value, 10);
182
- if (isNaN(value) || value < 1 || value > pageCount()) {
183
- currentPage(_currentPageIndex);
184
- currentPage.notifySubscribers();
185
- return;
186
- }
187
- currentPage(value);
188
- refreshEvents((_currentPageIndex - 1) * pageSize());
189
- return false;
190
- }
191
- return true;
192
- }
193
-
194
- function onRefreshEvents() {
195
- refreshEvents(0, blogId());
196
- }
197
-
198
- function onApplyPageSize() {
199
- applyPageSize();
200
- }
201
-
202
- function onApplySorting(columnItem) {
203
- applySorting(columnItem);
204
- }
205
-
206
-
207
- function applyPageSize(){
208
- var newPageSize = parseInt(selectedPageSize(), 10);
209
- pageSize(newPageSize);
210
- var secureCookie = false;
211
- if (window.location.href.indexOf('https://') > -1) {
212
- secureCookie = true;
213
- }
214
- $.cookie('wpph_ck_page_size', newPageSize, {secure: secureCookie});
215
- refreshEvents(0);
216
- }
217
-
218
- function applySorting(columnInfo) {
219
- if (orderBy() === columnInfo.columnName) {
220
- orderByDescending(! orderByDescending());
221
- }
222
- else {
223
- orderBy(columnInfo.columnName);
224
- orderByDescending(false);
225
- }
226
- refreshEvents(0);
227
- }
228
-
229
-
230
- function nextPage() {
231
- var currentOffset = offset();
232
- var newOffset = currentOffset + pageSize();
233
-
234
- if (newOffset < totalEventsCount()) {
235
- refreshEvents(newOffset);
236
- }
237
- }
238
-
239
- function prevPage() {
240
- var currentOffset = offset();
241
- var newOffset = currentOffset - pageSize();
242
-
243
- if (newOffset >= 0) {
244
- refreshEvents(newOffset);
245
- }
246
- }
247
-
248
- function firstPage() {
249
- if (offset() > 0)
250
- refreshEvents(0);
251
- }
252
-
253
- function lastPage() {
254
- var offset = Math.min(
255
- totalEventsCount(),
256
- pageSize() * (pageCount() - 1)
257
- );
258
- if (offset() !== offset)
259
- refreshEvents(offset);
260
- }
261
-
262
-
263
- function refreshEvents(offset, bid) {
264
- if (loading()) {
265
- console.warn('Cannot refresh events. Still busy!!!');
266
- return;
267
- }
268
- // console.warn('refreshEvents: %d', offset);
269
- if(bid !== null){
270
- loadRemoteData(offset, bid);
271
- }
272
- else { loadRemoteData(offset); }
273
- }
274
-
275
-
276
- return {
277
- columns: columns,
278
- error: error,
279
- loading: loading,
280
- events: events,
281
- totalEventsCount: totalEventsCount,
282
- offset: offset,
283
- availablePageSize: availablePageSize,
284
-
285
- isMainSite: isMainSite,
286
- blogId: blogId,
287
- blogList: blogList,
288
-
289
- pageSize: pageSize,
290
- selectedPageSize: selectedPageSize,
291
- pageCount: pageCount,
292
-
293
-
294
- currentPage: currentPage,
295
- orderBy: orderBy,
296
- orderByDescending: orderByDescending,
297
-
298
- onCurrentPageInputKeyDown: onCurrentPageInputKeyDown,
299
- onRefreshEvents: onRefreshEvents,
300
- onApplyPageSize: onApplyPageSize,
301
- onApplySorting: onApplySorting,
302
-
303
- nextPage: nextPage,
304
- prevPage: prevPage,
305
- firstPage: firstPage,
306
- lastPage: lastPage,
307
-
308
- applyPageSize: applyPageSize,
309
- applySorting: applySorting,
310
- refreshEvents: refreshEvents
311
- };
312
-
313
- })(window.jQuery, window.ko);
314
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
res/js/jquery-ck.js DELETED
@@ -1,95 +0,0 @@
1
- /*!
2
- * jQuery Cookie Plugin v1.3.1
3
- * https://github.com/carhartl/jquery-cookie
4
- *
5
- * Copyright 2013 Klaus Hartl
6
- * Released under the MIT license
7
- */
8
- (function (factory) {
9
- if (typeof define === 'function' && define.amd) {
10
- // AMD. Register as anonymous module.
11
- define(['jquery'], factory);
12
- } else {
13
- // Browser globals.
14
- factory(jQuery);
15
- }
16
- }(function ($) {
17
-
18
- var pluses = /\+/g;
19
-
20
- function raw(s) {
21
- return s;
22
- }
23
-
24
- function decoded(s) {
25
- return decodeURIComponent(s.replace(pluses, ' '));
26
- }
27
-
28
- function converted(s) {
29
- if (s.indexOf('"') === 0) {
30
- // This is a quoted cookie as according to RFC2068, unescape
31
- s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
32
- }
33
- try {
34
- return config.json ? JSON.parse(s) : s;
35
- } catch(er) {}
36
- }
37
-
38
- var config = $.cookie = function (key, value, options) {
39
-
40
- // write
41
- if (value !== undefined) {
42
- options = $.extend({}, config.defaults, options);
43
-
44
- if (typeof options.expires === 'number') {
45
- var days = options.expires, t = options.expires = new Date();
46
- t.setDate(t.getDate() + days);
47
- }
48
-
49
- value = config.json ? JSON.stringify(value) : String(value);
50
-
51
- return (document.cookie = [
52
- config.raw ? key : encodeURIComponent(key),
53
- '=',
54
- config.raw ? value : encodeURIComponent(value),
55
- options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
56
- options.path ? '; path=' + options.path : '',
57
- options.domain ? '; domain=' + options.domain : '',
58
- options.secure ? '; secure' : ''
59
- ].join(''));
60
- }
61
-
62
- // read
63
- var decode = config.raw ? raw : decoded;
64
- var cookies = document.cookie.split('; ');
65
- var result = key ? undefined : {};
66
- for (var i = 0, l = cookies.length; i < l; i++) {
67
- var parts = cookies[i].split('=');
68
- var name = decode(parts.shift());
69
- var cookie = decode(parts.join('='));
70
-
71
- if (key && key === name) {
72
- result = converted(cookie);
73
- break;
74
- }
75
-
76
- if (!key) {
77
- result[name] = converted(cookie);
78
- }
79
- }
80
-
81
- return result;
82
- };
83
-
84
- config.defaults = {};
85
-
86
- $.removeCookie = function (key, options) {
87
- if ($.cookie(key) !== undefined) {
88
- // Must not alter options, thus extending a fresh object...
89
- $.cookie(key, '', $.extend({}, options, { expires: -1 }));
90
- return true;
91
- }
92
- return false;
93
- };
94
-
95
- }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
res/js/knockout.js DELETED
@@ -1,85 +0,0 @@
1
- // Knockout JavaScript library v2.2.1
2
- // (c) Steven Sanderson - http://knockoutjs.com/
3
- // License: MIT (http://www.opensource.org/licenses/mit-license.php)
4
-
5
- (function() {function j(w){throw w;}var m=!0,p=null,r=!1;function u(w){return function(){return w}};var x=window,y=document,ga=navigator,F=window.jQuery,I=void 0;
6
- function L(w){function ha(a,d,c,e,f){var g=[];a=b.j(function(){var a=d(c,f)||[];0<g.length&&(b.a.Ya(M(g),a),e&&b.r.K(e,p,[c,a,f]));g.splice(0,g.length);b.a.P(g,a)},p,{W:a,Ka:function(){return 0==g.length||!b.a.X(g[0])}});return{M:g,j:a.pa()?a:I}}function M(a){for(;a.length&&!b.a.X(a[0]);)a.splice(0,1);if(1<a.length){for(var d=a[0],c=a[a.length-1],e=[d];d!==c;){d=d.nextSibling;if(!d)return;e.push(d)}Array.prototype.splice.apply(a,[0,a.length].concat(e))}return a}function S(a,b,c,e,f){var g=Math.min,
7
- h=Math.max,k=[],l,n=a.length,q,s=b.length,v=s-n||1,G=n+s+1,J,A,z;for(l=0;l<=n;l++){A=J;k.push(J=[]);z=g(s,l+v);for(q=h(0,l-1);q<=z;q++)J[q]=q?l?a[l-1]===b[q-1]?A[q-1]:g(A[q]||G,J[q-1]||G)+1:q+1:l+1}g=[];h=[];v=[];l=n;for(q=s;l||q;)s=k[l][q]-1,q&&s===k[l][q-1]?h.push(g[g.length]={status:c,value:b[--q],index:q}):l&&s===k[l-1][q]?v.push(g[g.length]={status:e,value:a[--l],index:l}):(g.push({status:"retained",value:b[--q]}),--l);if(h.length&&v.length){a=10*n;var t;for(b=c=0;(f||b<a)&&(t=h[c]);c++){for(e=
8
- 0;k=v[e];e++)if(t.value===k.value){t.moved=k.index;k.moved=t.index;v.splice(e,1);b=e=0;break}b+=e}}return g.reverse()}function T(a,d,c,e,f){f=f||{};var g=a&&N(a),g=g&&g.ownerDocument,h=f.templateEngine||O;b.za.vb(c,h,g);c=h.renderTemplate(c,e,f,g);("number"!=typeof c.length||0<c.length&&"number"!=typeof c[0].nodeType)&&j(Error("Template engine must return an array of DOM nodes"));g=r;switch(d){case "replaceChildren":b.e.N(a,c);g=m;break;case "replaceNode":b.a.Ya(a,c);g=m;break;case "ignoreTargetNode":break;
9
- default:j(Error("Unknown renderMode: "+d))}g&&(U(c,e),f.afterRender&&b.r.K(f.afterRender,p,[c,e.$data]));return c}function N(a){return a.nodeType?a:0<a.length?a[0]:p}function U(a,d){if(a.length){var c=a[0],e=a[a.length-1];V(c,e,function(a){b.Da(d,a)});V(c,e,function(a){b.s.ib(a,[d])})}}function V(a,d,c){var e;for(d=b.e.nextSibling(d);a&&(e=a)!==d;)a=b.e.nextSibling(e),(1===e.nodeType||8===e.nodeType)&&c(e)}function W(a,d,c){a=b.g.aa(a);for(var e=b.g.Q,f=0;f<a.length;f++){var g=a[f].key;if(e.hasOwnProperty(g)){var h=
10
- e[g];"function"===typeof h?(g=h(a[f].value))&&j(Error(g)):h||j(Error("This template engine does not support the '"+g+"' binding within its templates"))}}a="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+b.g.ba(a)+" } })()})";return c.createJavaScriptEvaluatorBlock(a)+d}function X(a,d,c,e){function f(a){return function(){return k[a]}}function g(){return k}var h=0,k,l;b.j(function(){var n=c&&c instanceof b.z?c:new b.z(b.a.d(c)),q=n.$data;e&&b.eb(a,n);if(k=("function"==typeof d?
11
- d(n,a):d)||b.J.instance.getBindings(a,n)){if(0===h){h=1;for(var s in k){var v=b.c[s];v&&8===a.nodeType&&!b.e.I[s]&&j(Error("The binding '"+s+"' cannot be used with virtual elements"));if(v&&"function"==typeof v.init&&(v=(0,v.init)(a,f(s),g,q,n))&&v.controlsDescendantBindings)l!==I&&j(Error("Multiple bindings ("+l+" and "+s+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.")),l=s}h=2}if(2===h)for(s in k)(v=b.c[s])&&"function"==
12
- typeof v.update&&(0,v.update)(a,f(s),g,q,n)}},p,{W:a});return{Nb:l===I}}function Y(a,d,c){var e=m,f=1===d.nodeType;f&&b.e.Ta(d);if(f&&c||b.J.instance.nodeHasBindings(d))e=X(d,p,a,c).Nb;e&&Z(a,d,!f)}function Z(a,d,c){for(var e=b.e.firstChild(d);d=e;)e=b.e.nextSibling(d),Y(a,d,c)}function $(a,b){var c=aa(a,b);return c?0<c.length?c[c.length-1].nextSibling:a.nextSibling:p}function aa(a,b){for(var c=a,e=1,f=[];c=c.nextSibling;){if(H(c)&&(e--,0===e))return f;f.push(c);B(c)&&e++}b||j(Error("Cannot find closing comment tag to match: "+
13
- a.nodeValue));return p}function H(a){return 8==a.nodeType&&(K?a.text:a.nodeValue).match(ia)}function B(a){return 8==a.nodeType&&(K?a.text:a.nodeValue).match(ja)}function P(a,b){for(var c=p;a!=c;)c=a,a=a.replace(ka,function(a,c){return b[c]});return a}function la(){var a=[],d=[];this.save=function(c,e){var f=b.a.i(a,c);0<=f?d[f]=e:(a.push(c),d.push(e))};this.get=function(c){c=b.a.i(a,c);return 0<=c?d[c]:I}}function ba(a,b,c){function e(e){var g=b(a[e]);switch(typeof g){case "boolean":case "number":case "string":case "function":f[e]=
14
- g;break;case "object":case "undefined":var h=c.get(g);f[e]=h!==I?h:ba(g,b,c)}}c=c||new la;a=b(a);if(!("object"==typeof a&&a!==p&&a!==I&&!(a instanceof Date)))return a;var f=a instanceof Array?[]:{};c.save(a,f);var g=a;if(g instanceof Array){for(var h=0;h<g.length;h++)e(h);"function"==typeof g.toJSON&&e("toJSON")}else for(h in g)e(h);return f}function ca(a,d){if(a)if(8==a.nodeType){var c=b.s.Ua(a.nodeValue);c!=p&&d.push({sb:a,Fb:c})}else if(1==a.nodeType)for(var c=0,e=a.childNodes,f=e.length;c<f;c++)ca(e[c],
15
- d)}function Q(a,d,c,e){b.c[a]={init:function(a){b.a.f.set(a,da,{});return{controlsDescendantBindings:m}},update:function(a,g,h,k,l){h=b.a.f.get(a,da);g=b.a.d(g());k=!c!==!g;var n=!h.Za;if(n||d||k!==h.qb)n&&(h.Za=b.a.Ia(b.e.childNodes(a),m)),k?(n||b.e.N(a,b.a.Ia(h.Za)),b.Ea(e?e(l,g):l,a)):b.e.Y(a),h.qb=k}};b.g.Q[a]=r;b.e.I[a]=m}function ea(a,d,c){c&&d!==b.k.q(a)&&b.k.T(a,d);d!==b.k.q(a)&&b.r.K(b.a.Ba,p,[a,"change"])}var b="undefined"!==typeof w?w:{};b.b=function(a,d){for(var c=a.split("."),e=b,f=0;f<
16
- c.length-1;f++)e=e[c[f]];e[c[c.length-1]]=d};b.p=function(a,b,c){a[b]=c};b.version="2.2.1";b.b("version",b.version);b.a=new function(){function a(a,d){if("input"!==b.a.u(a)||!a.type||"click"!=d.toLowerCase())return r;var c=a.type;return"checkbox"==c||"radio"==c}var d=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,c={},e={};c[/Firefox\/2/i.test(ga.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];c.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");
17
- for(var f in c){var g=c[f];if(g.length)for(var h=0,k=g.length;h<k;h++)e[g[h]]=f}var l={propertychange:m},n,c=3;f=y.createElement("div");for(g=f.getElementsByTagName("i");f.innerHTML="\x3c!--[if gt IE "+ ++c+"]><i></i><![endif]--\x3e",g[0];);n=4<c?c:I;return{Na:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],o:function(a,b){for(var d=0,c=a.length;d<c;d++)b(a[d])},i:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var d=0,c=a.length;d<
18
- c;d++)if(a[d]===b)return d;return-1},lb:function(a,b,d){for(var c=0,e=a.length;c<e;c++)if(b.call(d,a[c]))return a[c];return p},ga:function(a,d){var c=b.a.i(a,d);0<=c&&a.splice(c,1)},Ga:function(a){a=a||[];for(var d=[],c=0,e=a.length;c<e;c++)0>b.a.i(d,a[c])&&d.push(a[c]);return d},V:function(a,b){a=a||[];for(var d=[],c=0,e=a.length;c<e;c++)d.push(b(a[c]));return d},fa:function(a,b){a=a||[];for(var d=[],c=0,e=a.length;c<e;c++)b(a[c])&&d.push(a[c]);return d},P:function(a,b){if(b instanceof Array)a.push.apply(a,
19
- b);else for(var d=0,c=b.length;d<c;d++)a.push(b[d]);return a},extend:function(a,b){if(b)for(var d in b)b.hasOwnProperty(d)&&(a[d]=b[d]);return a},ka:function(a){for(;a.firstChild;)b.removeNode(a.firstChild)},Hb:function(a){a=b.a.L(a);for(var d=y.createElement("div"),c=0,e=a.length;c<e;c++)d.appendChild(b.A(a[c]));return d},Ia:function(a,d){for(var c=0,e=a.length,g=[];c<e;c++){var f=a[c].cloneNode(m);g.push(d?b.A(f):f)}return g},N:function(a,d){b.a.ka(a);if(d)for(var c=0,e=d.length;c<e;c++)a.appendChild(d[c])},
20
- Ya:function(a,d){var c=a.nodeType?[a]:a;if(0<c.length){for(var e=c[0],g=e.parentNode,f=0,h=d.length;f<h;f++)g.insertBefore(d[f],e);f=0;for(h=c.length;f<h;f++)b.removeNode(c[f])}},bb:function(a,b){7>n?a.setAttribute("selected",b):a.selected=b},D:function(a){return(a||"").replace(d,"")},Rb:function(a,d){for(var c=[],e=(a||"").split(d),f=0,g=e.length;f<g;f++){var h=b.a.D(e[f]);""!==h&&c.push(h)}return c},Ob:function(a,b){a=a||"";return b.length>a.length?r:a.substring(0,b.length)===b},tb:function(a,b){if(b.compareDocumentPosition)return 16==
21
- (b.compareDocumentPosition(a)&16);for(;a!=p;){if(a==b)return m;a=a.parentNode}return r},X:function(a){return b.a.tb(a,a.ownerDocument)},u:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},n:function(b,d,c){var e=n&&l[d];if(!e&&"undefined"!=typeof F){if(a(b,d)){var f=c;c=function(a,b){var d=this.checked;b&&(this.checked=b.nb!==m);f.call(this,a);this.checked=d}}F(b).bind(d,c)}else!e&&"function"==typeof b.addEventListener?b.addEventListener(d,c,r):"undefined"!=typeof b.attachEvent?b.attachEvent("on"+
22
- d,function(a){c.call(b,a)}):j(Error("Browser doesn't support addEventListener or attachEvent"))},Ba:function(b,d){(!b||!b.nodeType)&&j(Error("element must be a DOM node when calling triggerEvent"));if("undefined"!=typeof F){var c=[];a(b,d)&&c.push({nb:b.checked});F(b).trigger(d,c)}else"function"==typeof y.createEvent?"function"==typeof b.dispatchEvent?(c=y.createEvent(e[d]||"HTMLEvents"),c.initEvent(d,m,m,x,0,0,0,0,0,r,r,r,r,0,b),b.dispatchEvent(c)):j(Error("The supplied element doesn't support dispatchEvent")):
23
- "undefined"!=typeof b.fireEvent?(a(b,d)&&(b.checked=b.checked!==m),b.fireEvent("on"+d)):j(Error("Browser doesn't support triggering events"))},d:function(a){return b.$(a)?a():a},ua:function(a){return b.$(a)?a.t():a},da:function(a,d,c){if(d){var e=/[\w-]+/g,f=a.className.match(e)||[];b.a.o(d.match(e),function(a){var d=b.a.i(f,a);0<=d?c||f.splice(d,1):c&&f.push(a)});a.className=f.join(" ")}},cb:function(a,d){var c=b.a.d(d);if(c===p||c===I)c="";if(3===a.nodeType)a.data=c;else{var e=b.e.firstChild(a);
24
- !e||3!=e.nodeType||b.e.nextSibling(e)?b.e.N(a,[y.createTextNode(c)]):e.data=c;b.a.wb(a)}},ab:function(a,b){a.name=b;if(7>=n)try{a.mergeAttributes(y.createElement("<input name='"+a.name+"'/>"),r)}catch(d){}},wb:function(a){9<=n&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},ub:function(a){if(9<=n){var b=a.style.width;a.style.width=0;a.style.width=b}},Lb:function(a,d){a=b.a.d(a);d=b.a.d(d);for(var c=[],e=a;e<=d;e++)c.push(e);return c},L:function(a){for(var b=[],d=0,c=a.length;d<
25
- c;d++)b.push(a[d]);return b},Pb:6===n,Qb:7===n,Z:n,Oa:function(a,d){for(var c=b.a.L(a.getElementsByTagName("input")).concat(b.a.L(a.getElementsByTagName("textarea"))),e="string"==typeof d?function(a){return a.name===d}:function(a){return d.test(a.name)},f=[],g=c.length-1;0<=g;g--)e(c[g])&&f.push(c[g]);return f},Ib:function(a){return"string"==typeof a&&(a=b.a.D(a))?x.JSON&&x.JSON.parse?x.JSON.parse(a):(new Function("return "+a))():p},xa:function(a,d,c){("undefined"==typeof JSON||"undefined"==typeof JSON.stringify)&&
26
- j(Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js"));return JSON.stringify(b.a.d(a),d,c)},Jb:function(a,d,c){c=c||{};var e=c.params||{},f=c.includeFields||this.Na,g=a;if("object"==typeof a&&"form"===b.a.u(a))for(var g=a.action,h=f.length-1;0<=h;h--)for(var k=b.a.Oa(a,f[h]),l=k.length-1;0<=l;l--)e[k[l].name]=k[l].value;d=b.a.d(d);var n=y.createElement("form");
27
- n.style.display="none";n.action=g;n.method="post";for(var w in d)a=y.createElement("input"),a.name=w,a.value=b.a.xa(b.a.d(d[w])),n.appendChild(a);for(w in e)a=y.createElement("input"),a.name=w,a.value=e[w],n.appendChild(a);y.body.appendChild(n);c.submitter?c.submitter(n):n.submit();setTimeout(function(){n.parentNode.removeChild(n)},0)}}};b.b("utils",b.a);b.b("utils.arrayForEach",b.a.o);b.b("utils.arrayFirst",b.a.lb);b.b("utils.arrayFilter",b.a.fa);b.b("utils.arrayGetDistinctValues",b.a.Ga);b.b("utils.arrayIndexOf",
28
- b.a.i);b.b("utils.arrayMap",b.a.V);b.b("utils.arrayPushAll",b.a.P);b.b("utils.arrayRemoveItem",b.a.ga);b.b("utils.extend",b.a.extend);b.b("utils.fieldsIncludedWithJsonPost",b.a.Na);b.b("utils.getFormFields",b.a.Oa);b.b("utils.peekObservable",b.a.ua);b.b("utils.postJson",b.a.Jb);b.b("utils.parseJson",b.a.Ib);b.b("utils.registerEventHandler",b.a.n);b.b("utils.stringifyJson",b.a.xa);b.b("utils.range",b.a.Lb);b.b("utils.toggleDomNodeCssClass",b.a.da);b.b("utils.triggerEvent",b.a.Ba);b.b("utils.unwrapObservable",
29
- b.a.d);Function.prototype.bind||(Function.prototype.bind=function(a){var b=this,c=Array.prototype.slice.call(arguments);a=c.shift();return function(){return b.apply(a,c.concat(Array.prototype.slice.call(arguments)))}});b.a.f=new function(){var a=0,d="__ko__"+(new Date).getTime(),c={};return{get:function(a,d){var c=b.a.f.la(a,r);return c===I?I:c[d]},set:function(a,d,c){c===I&&b.a.f.la(a,r)===I||(b.a.f.la(a,m)[d]=c)},la:function(b,f){var g=b[d];if(!g||!("null"!==g&&c[g])){if(!f)return I;g=b[d]="ko"+
30
- a++;c[g]={}}return c[g]},clear:function(a){var b=a[d];return b?(delete c[b],a[d]=p,m):r}}};b.b("utils.domData",b.a.f);b.b("utils.domData.clear",b.a.f.clear);b.a.F=new function(){function a(a,d){var e=b.a.f.get(a,c);e===I&&d&&(e=[],b.a.f.set(a,c,e));return e}function d(c){var e=a(c,r);if(e)for(var e=e.slice(0),k=0;k<e.length;k++)e[k](c);b.a.f.clear(c);"function"==typeof F&&"function"==typeof F.cleanData&&F.cleanData([c]);if(f[c.nodeType])for(e=c.firstChild;c=e;)e=c.nextSibling,8===c.nodeType&&d(c)}
31
- var c="__ko_domNodeDisposal__"+(new Date).getTime(),e={1:m,8:m,9:m},f={1:m,9:m};return{Ca:function(b,d){"function"!=typeof d&&j(Error("Callback must be a function"));a(b,m).push(d)},Xa:function(d,e){var f=a(d,r);f&&(b.a.ga(f,e),0==f.length&&b.a.f.set(d,c,I))},A:function(a){if(e[a.nodeType]&&(d(a),f[a.nodeType])){var c=[];b.a.P(c,a.getElementsByTagName("*"));for(var k=0,l=c.length;k<l;k++)d(c[k])}return a},removeNode:function(a){b.A(a);a.parentNode&&a.parentNode.removeChild(a)}}};b.A=b.a.F.A;b.removeNode=
32
- b.a.F.removeNode;b.b("cleanNode",b.A);b.b("removeNode",b.removeNode);b.b("utils.domNodeDisposal",b.a.F);b.b("utils.domNodeDisposal.addDisposeCallback",b.a.F.Ca);b.b("utils.domNodeDisposal.removeDisposeCallback",b.a.F.Xa);b.a.ta=function(a){var d;if("undefined"!=typeof F)if(F.parseHTML)d=F.parseHTML(a);else{if((d=F.clean([a]))&&d[0]){for(a=d[0];a.parentNode&&11!==a.parentNode.nodeType;)a=a.parentNode;a.parentNode&&a.parentNode.removeChild(a)}}else{var c=b.a.D(a).toLowerCase();d=y.createElement("div");
33
- c=c.match(/^<(thead|tbody|tfoot)/)&&[1,"<table>","</table>"]||!c.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!c.indexOf("<td")||!c.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||[0,"",""];a="ignored<div>"+c[1]+a+c[2]+"</div>";for("function"==typeof x.innerShiv?d.appendChild(x.innerShiv(a)):d.innerHTML=a;c[0]--;)d=d.lastChild;d=b.a.L(d.lastChild.childNodes)}return d};b.a.ca=function(a,d){b.a.ka(a);d=b.a.d(d);if(d!==p&&d!==I)if("string"!=typeof d&&(d=d.toString()),
34
- "undefined"!=typeof F)F(a).html(d);else for(var c=b.a.ta(d),e=0;e<c.length;e++)a.appendChild(c[e])};b.b("utils.parseHtmlFragment",b.a.ta);b.b("utils.setHtml",b.a.ca);var R={};b.s={ra:function(a){"function"!=typeof a&&j(Error("You can only pass a function to ko.memoization.memoize()"));var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);R[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},hb:function(a,b){var c=R[a];c===I&&j(Error("Couldn't find any memo with ID "+
35
- a+". Perhaps it's already been unmemoized."));try{return c.apply(p,b||[]),m}finally{delete R[a]}},ib:function(a,d){var c=[];ca(a,c);for(var e=0,f=c.length;e<f;e++){var g=c[e].sb,h=[g];d&&b.a.P(h,d);b.s.hb(c[e].Fb,h);g.nodeValue="";g.parentNode&&g.parentNode.removeChild(g)}},Ua:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:p}};b.b("memoization",b.s);b.b("memoization.memoize",b.s.ra);b.b("memoization.unmemoize",b.s.hb);b.b("memoization.parseMemoText",b.s.Ua);b.b("memoization.unmemoizeDomNodeAndDescendants",
36
- b.s.ib);b.Ma={throttle:function(a,d){a.throttleEvaluation=d;var c=p;return b.j({read:a,write:function(b){clearTimeout(c);c=setTimeout(function(){a(b)},d)}})},notify:function(a,d){a.equalityComparer="always"==d?u(r):b.m.fn.equalityComparer;return a}};b.b("extenders",b.Ma);b.fb=function(a,d,c){this.target=a;this.ha=d;this.rb=c;b.p(this,"dispose",this.B)};b.fb.prototype.B=function(){this.Cb=m;this.rb()};b.S=function(){this.w={};b.a.extend(this,b.S.fn);b.p(this,"subscribe",this.ya);b.p(this,"extend",
37
- this.extend);b.p(this,"getSubscriptionsCount",this.yb)};b.S.fn={ya:function(a,d,c){c=c||"change";var e=new b.fb(this,d?a.bind(d):a,function(){b.a.ga(this.w[c],e)}.bind(this));this.w[c]||(this.w[c]=[]);this.w[c].push(e);return e},notifySubscribers:function(a,d){d=d||"change";this.w[d]&&b.r.K(function(){b.a.o(this.w[d].slice(0),function(b){b&&b.Cb!==m&&b.ha(a)})},this)},yb:function(){var a=0,b;for(b in this.w)this.w.hasOwnProperty(b)&&(a+=this.w[b].length);return a},extend:function(a){var d=this;if(a)for(var c in a){var e=
38
- b.Ma[c];"function"==typeof e&&(d=e(d,a[c]))}return d}};b.Qa=function(a){return"function"==typeof a.ya&&"function"==typeof a.notifySubscribers};b.b("subscribable",b.S);b.b("isSubscribable",b.Qa);var C=[];b.r={mb:function(a){C.push({ha:a,La:[]})},end:function(){C.pop()},Wa:function(a){b.Qa(a)||j(Error("Only subscribable things can act as dependencies"));if(0<C.length){var d=C[C.length-1];d&&!(0<=b.a.i(d.La,a))&&(d.La.push(a),d.ha(a))}},K:function(a,b,c){try{return C.push(p),a.apply(b,c||[])}finally{C.pop()}}};
39
- var ma={undefined:m,"boolean":m,number:m,string:m};b.m=function(a){function d(){if(0<arguments.length){if(!d.equalityComparer||!d.equalityComparer(c,arguments[0]))d.H(),c=arguments[0],d.G();return this}b.r.Wa(d);return c}var c=a;b.S.call(d);d.t=function(){return c};d.G=function(){d.notifySubscribers(c)};d.H=function(){d.notifySubscribers(c,"beforeChange")};b.a.extend(d,b.m.fn);b.p(d,"peek",d.t);b.p(d,"valueHasMutated",d.G);b.p(d,"valueWillMutate",d.H);return d};b.m.fn={equalityComparer:function(a,
40
- b){return a===p||typeof a in ma?a===b:r}};var E=b.m.Kb="__ko_proto__";b.m.fn[E]=b.m;b.ma=function(a,d){return a===p||a===I||a[E]===I?r:a[E]===d?m:b.ma(a[E],d)};b.$=function(a){return b.ma(a,b.m)};b.Ra=function(a){return"function"==typeof a&&a[E]===b.m||"function"==typeof a&&a[E]===b.j&&a.zb?m:r};b.b("observable",b.m);b.b("isObservable",b.$);b.b("isWriteableObservable",b.Ra);b.R=function(a){0==arguments.length&&(a=[]);a!==p&&(a!==I&&!("length"in a))&&j(Error("The argument passed when initializing an observable array must be an array, or null, or undefined."));
41
- var d=b.m(a);b.a.extend(d,b.R.fn);return d};b.R.fn={remove:function(a){for(var b=this.t(),c=[],e="function"==typeof a?a:function(b){return b===a},f=0;f<b.length;f++){var g=b[f];e(g)&&(0===c.length&&this.H(),c.push(g),b.splice(f,1),f--)}c.length&&this.G();return c},removeAll:function(a){if(a===I){var d=this.t(),c=d.slice(0);this.H();d.splice(0,d.length);this.G();return c}return!a?[]:this.remove(function(d){return 0<=b.a.i(a,d)})},destroy:function(a){var b=this.t(),c="function"==typeof a?a:function(b){return b===
42
- a};this.H();for(var e=b.length-1;0<=e;e--)c(b[e])&&(b[e]._destroy=m);this.G()},destroyAll:function(a){return a===I?this.destroy(u(m)):!a?[]:this.destroy(function(d){return 0<=b.a.i(a,d)})},indexOf:function(a){var d=this();return b.a.i(d,a)},replace:function(a,b){var c=this.indexOf(a);0<=c&&(this.H(),this.t()[c]=b,this.G())}};b.a.o("pop push reverse shift sort splice unshift".split(" "),function(a){b.R.fn[a]=function(){var b=this.t();this.H();b=b[a].apply(b,arguments);this.G();return b}});b.a.o(["slice"],
43
- function(a){b.R.fn[a]=function(){var b=this();return b[a].apply(b,arguments)}});b.b("observableArray",b.R);b.j=function(a,d,c){function e(){b.a.o(z,function(a){a.B()});z=[]}function f(){var a=h.throttleEvaluation;a&&0<=a?(clearTimeout(t),t=setTimeout(g,a)):g()}function g(){if(!q)if(n&&w())A();else{q=m;try{var a=b.a.V(z,function(a){return a.target});b.r.mb(function(c){var d;0<=(d=b.a.i(a,c))?a[d]=I:z.push(c.ya(f))});for(var c=s.call(d),e=a.length-1;0<=e;e--)a[e]&&z.splice(e,1)[0].B();n=m;h.notifySubscribers(l,
44
- "beforeChange");l=c}finally{b.r.end()}h.notifySubscribers(l);q=r;z.length||A()}}function h(){if(0<arguments.length)return"function"===typeof v?v.apply(d,arguments):j(Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.")),this;n||g();b.r.Wa(h);return l}function k(){return!n||0<z.length}var l,n=r,q=r,s=a;s&&"object"==typeof s?(c=s,s=c.read):(c=c||{},s||(s=c.read));"function"!=typeof s&&j(Error("Pass a function that returns the value of the ko.computed"));
45
- var v=c.write,G=c.disposeWhenNodeIsRemoved||c.W||p,w=c.disposeWhen||c.Ka||u(r),A=e,z=[],t=p;d||(d=c.owner);h.t=function(){n||g();return l};h.xb=function(){return z.length};h.zb="function"===typeof c.write;h.B=function(){A()};h.pa=k;b.S.call(h);b.a.extend(h,b.j.fn);b.p(h,"peek",h.t);b.p(h,"dispose",h.B);b.p(h,"isActive",h.pa);b.p(h,"getDependenciesCount",h.xb);c.deferEvaluation!==m&&g();if(G&&k()){A=function(){b.a.F.Xa(G,arguments.callee);e()};b.a.F.Ca(G,A);var D=w,w=function(){return!b.a.X(G)||D()}}return h};
46
- b.Bb=function(a){return b.ma(a,b.j)};w=b.m.Kb;b.j[w]=b.m;b.j.fn={};b.j.fn[w]=b.j;b.b("dependentObservable",b.j);b.b("computed",b.j);b.b("isComputed",b.Bb);b.gb=function(a){0==arguments.length&&j(Error("When calling ko.toJS, pass the object you want to convert."));return ba(a,function(a){for(var c=0;b.$(a)&&10>c;c++)a=a();return a})};b.toJSON=function(a,d,c){a=b.gb(a);return b.a.xa(a,d,c)};b.b("toJS",b.gb);b.b("toJSON",b.toJSON);b.k={q:function(a){switch(b.a.u(a)){case "option":return a.__ko__hasDomDataOptionValue__===
47
- m?b.a.f.get(a,b.c.options.sa):7>=b.a.Z?a.getAttributeNode("value").specified?a.value:a.text:a.value;case "select":return 0<=a.selectedIndex?b.k.q(a.options[a.selectedIndex]):I;default:return a.value}},T:function(a,d){switch(b.a.u(a)){case "option":switch(typeof d){case "string":b.a.f.set(a,b.c.options.sa,I);"__ko__hasDomDataOptionValue__"in a&&delete a.__ko__hasDomDataOptionValue__;a.value=d;break;default:b.a.f.set(a,b.c.options.sa,d),a.__ko__hasDomDataOptionValue__=m,a.value="number"===typeof d?
48
- d:""}break;case "select":for(var c=a.options.length-1;0<=c;c--)if(b.k.q(a.options[c])==d){a.selectedIndex=c;break}break;default:if(d===p||d===I)d="";a.value=d}}};b.b("selectExtensions",b.k);b.b("selectExtensions.readValue",b.k.q);b.b("selectExtensions.writeValue",b.k.T);var ka=/\@ko_token_(\d+)\@/g,na=["true","false"],oa=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;b.g={Q:[],aa:function(a){var d=b.a.D(a);if(3>d.length)return[];"{"===d.charAt(0)&&(d=d.substring(1,d.length-1));a=[];for(var c=
49
- p,e,f=0;f<d.length;f++){var g=d.charAt(f);if(c===p)switch(g){case '"':case "'":case "/":c=f,e=g}else if(g==e&&"\\"!==d.charAt(f-1)){g=d.substring(c,f+1);a.push(g);var h="@ko_token_"+(a.length-1)+"@",d=d.substring(0,c)+h+d.substring(f+1),f=f-(g.length-h.length),c=p}}e=c=p;for(var k=0,l=p,f=0;f<d.length;f++){g=d.charAt(f);if(c===p)switch(g){case "{":c=f;l=g;e="}";break;case "(":c=f;l=g;e=")";break;case "[":c=f,l=g,e="]"}g===l?k++:g===e&&(k--,0===k&&(g=d.substring(c,f+1),a.push(g),h="@ko_token_"+(a.length-
50
- 1)+"@",d=d.substring(0,c)+h+d.substring(f+1),f-=g.length-h.length,c=p))}e=[];d=d.split(",");c=0;for(f=d.length;c<f;c++)k=d[c],l=k.indexOf(":"),0<l&&l<k.length-1?(g=k.substring(l+1),e.push({key:P(k.substring(0,l),a),value:P(g,a)})):e.push({unknown:P(k,a)});return e},ba:function(a){var d="string"===typeof a?b.g.aa(a):a,c=[];a=[];for(var e,f=0;e=d[f];f++)if(0<c.length&&c.push(","),e.key){var g;a:{g=e.key;var h=b.a.D(g);switch(h.length&&h.charAt(0)){case "'":case '"':break a;default:g="'"+h+"'"}}e=e.value;
51
- c.push(g);c.push(":");c.push(e);e=b.a.D(e);0<=b.a.i(na,b.a.D(e).toLowerCase())?e=r:(h=e.match(oa),e=h===p?r:h[1]?"Object("+h[1]+")"+h[2]:e);e&&(0<a.length&&a.push(", "),a.push(g+" : function(__ko_value) { "+e+" = __ko_value; }"))}else e.unknown&&c.push(e.unknown);d=c.join("");0<a.length&&(d=d+", '_ko_property_writers' : { "+a.join("")+" } ");return d},Eb:function(a,d){for(var c=0;c<a.length;c++)if(b.a.D(a[c].key)==d)return m;return r},ea:function(a,d,c,e,f){if(!a||!b.Ra(a)){if((a=d()._ko_property_writers)&&
52
- a[c])a[c](e)}else(!f||a.t()!==e)&&a(e)}};b.b("expressionRewriting",b.g);b.b("expressionRewriting.bindingRewriteValidators",b.g.Q);b.b("expressionRewriting.parseObjectLiteral",b.g.aa);b.b("expressionRewriting.preProcessBindings",b.g.ba);b.b("jsonExpressionRewriting",b.g);b.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",b.g.ba);var K="\x3c!--test--\x3e"===y.createComment("test").text,ja=K?/^\x3c!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*--\x3e$/:/^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/,ia=K?/^\x3c!--\s*\/ko\s*--\x3e$/:
53
- /^\s*\/ko\s*$/,pa={ul:m,ol:m};b.e={I:{},childNodes:function(a){return B(a)?aa(a):a.childNodes},Y:function(a){if(B(a)){a=b.e.childNodes(a);for(var d=0,c=a.length;d<c;d++)b.removeNode(a[d])}else b.a.ka(a)},N:function(a,d){if(B(a)){b.e.Y(a);for(var c=a.nextSibling,e=0,f=d.length;e<f;e++)c.parentNode.insertBefore(d[e],c)}else b.a.N(a,d)},Va:function(a,b){B(a)?a.parentNode.insertBefore(b,a.nextSibling):a.firstChild?a.insertBefore(b,a.firstChild):a.appendChild(b)},Pa:function(a,d,c){c?B(a)?a.parentNode.insertBefore(d,
54
- c.nextSibling):c.nextSibling?a.insertBefore(d,c.nextSibling):a.appendChild(d):b.e.Va(a,d)},firstChild:function(a){return!B(a)?a.firstChild:!a.nextSibling||H(a.nextSibling)?p:a.nextSibling},nextSibling:function(a){B(a)&&(a=$(a));return a.nextSibling&&H(a.nextSibling)?p:a.nextSibling},jb:function(a){return(a=B(a))?a[1]:p},Ta:function(a){if(pa[b.a.u(a)]){var d=a.firstChild;if(d){do if(1===d.nodeType){var c;c=d.firstChild;var e=p;if(c){do if(e)e.push(c);else if(B(c)){var f=$(c,m);f?c=f:e=[c]}else H(c)&&
55
- (e=[c]);while(c=c.nextSibling)}if(c=e){e=d.nextSibling;for(f=0;f<c.length;f++)e?a.insertBefore(c[f],e):a.appendChild(c[f])}}while(d=d.nextSibling)}}}};b.b("virtualElements",b.e);b.b("virtualElements.allowedBindings",b.e.I);b.b("virtualElements.emptyNode",b.e.Y);b.b("virtualElements.insertAfter",b.e.Pa);b.b("virtualElements.prepend",b.e.Va);b.b("virtualElements.setDomNodeChildren",b.e.N);b.J=function(){this.Ha={}};b.a.extend(b.J.prototype,{nodeHasBindings:function(a){switch(a.nodeType){case 1:return a.getAttribute("data-bind")!=
56
- p;case 8:return b.e.jb(a)!=p;default:return r}},getBindings:function(a,b){var c=this.getBindingsString(a,b);return c?this.parseBindingsString(c,b,a):p},getBindingsString:function(a){switch(a.nodeType){case 1:return a.getAttribute("data-bind");case 8:return b.e.jb(a);default:return p}},parseBindingsString:function(a,d,c){try{var e;if(!(e=this.Ha[a])){var f=this.Ha,g,h="with($context){with($data||{}){return{"+b.g.ba(a)+"}}}";g=new Function("$context","$element",h);e=f[a]=g}return e(d,c)}catch(k){j(Error("Unable to parse bindings.\nMessage: "+
57
- k+";\nBindings value: "+a))}}});b.J.instance=new b.J;b.b("bindingProvider",b.J);b.c={};b.z=function(a,d,c){d?(b.a.extend(this,d),this.$parentContext=d,this.$parent=d.$data,this.$parents=(d.$parents||[]).slice(0),this.$parents.unshift(this.$parent)):(this.$parents=[],this.$root=a,this.ko=b);this.$data=a;c&&(this[c]=a)};b.z.prototype.createChildContext=function(a,d){return new b.z(a,this,d)};b.z.prototype.extend=function(a){var d=b.a.extend(new b.z,this);return b.a.extend(d,a)};b.eb=function(a,d){if(2==
58
- arguments.length)b.a.f.set(a,"__ko_bindingContext__",d);else return b.a.f.get(a,"__ko_bindingContext__")};b.Fa=function(a,d,c){1===a.nodeType&&b.e.Ta(a);return X(a,d,c,m)};b.Ea=function(a,b){(1===b.nodeType||8===b.nodeType)&&Z(a,b,m)};b.Da=function(a,b){b&&(1!==b.nodeType&&8!==b.nodeType)&&j(Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node"));b=b||x.document.body;Y(a,b,m)};b.ja=function(a){switch(a.nodeType){case 1:case 8:var d=b.eb(a);if(d)return d;
59
- if(a.parentNode)return b.ja(a.parentNode)}return I};b.pb=function(a){return(a=b.ja(a))?a.$data:I};b.b("bindingHandlers",b.c);b.b("applyBindings",b.Da);b.b("applyBindingsToDescendants",b.Ea);b.b("applyBindingsToNode",b.Fa);b.b("contextFor",b.ja);b.b("dataFor",b.pb);var fa={"class":"className","for":"htmlFor"};b.c.attr={update:function(a,d){var c=b.a.d(d())||{},e;for(e in c)if("string"==typeof e){var f=b.a.d(c[e]),g=f===r||f===p||f===I;g&&a.removeAttribute(e);8>=b.a.Z&&e in fa?(e=fa[e],g?a.removeAttribute(e):
60
- a[e]=f):g||a.setAttribute(e,f.toString());"name"===e&&b.a.ab(a,g?"":f.toString())}}};b.c.checked={init:function(a,d,c){b.a.n(a,"click",function(){var e;if("checkbox"==a.type)e=a.checked;else if("radio"==a.type&&a.checked)e=a.value;else return;var f=d(),g=b.a.d(f);"checkbox"==a.type&&g instanceof Array?(e=b.a.i(g,a.value),a.checked&&0>e?f.push(a.value):!a.checked&&0<=e&&f.splice(e,1)):b.g.ea(f,c,"checked",e,m)});"radio"==a.type&&!a.name&&b.c.uniqueName.init(a,u(m))},update:function(a,d){var c=b.a.d(d());
61
- "checkbox"==a.type?a.checked=c instanceof Array?0<=b.a.i(c,a.value):c:"radio"==a.type&&(a.checked=a.value==c)}};b.c.css={update:function(a,d){var c=b.a.d(d());if("object"==typeof c)for(var e in c){var f=b.a.d(c[e]);b.a.da(a,e,f)}else c=String(c||""),b.a.da(a,a.__ko__cssValue,r),a.__ko__cssValue=c,b.a.da(a,c,m)}};b.c.enable={update:function(a,d){var c=b.a.d(d());c&&a.disabled?a.removeAttribute("disabled"):!c&&!a.disabled&&(a.disabled=m)}};b.c.disable={update:function(a,d){b.c.enable.update(a,function(){return!b.a.d(d())})}};
62
- b.c.event={init:function(a,d,c,e){var f=d()||{},g;for(g in f)(function(){var f=g;"string"==typeof f&&b.a.n(a,f,function(a){var g,n=d()[f];if(n){var q=c();try{var s=b.a.L(arguments);s.unshift(e);g=n.apply(e,s)}finally{g!==m&&(a.preventDefault?a.preventDefault():a.returnValue=r)}q[f+"Bubble"]===r&&(a.cancelBubble=m,a.stopPropagation&&a.stopPropagation())}})})()}};b.c.foreach={Sa:function(a){return function(){var d=a(),c=b.a.ua(d);if(!c||"number"==typeof c.length)return{foreach:d,templateEngine:b.C.oa};
63
- b.a.d(d);return{foreach:c.data,as:c.as,includeDestroyed:c.includeDestroyed,afterAdd:c.afterAdd,beforeRemove:c.beforeRemove,afterRender:c.afterRender,beforeMove:c.beforeMove,afterMove:c.afterMove,templateEngine:b.C.oa}}},init:function(a,d){return b.c.template.init(a,b.c.foreach.Sa(d))},update:function(a,d,c,e,f){return b.c.template.update(a,b.c.foreach.Sa(d),c,e,f)}};b.g.Q.foreach=r;b.e.I.foreach=m;b.c.hasfocus={init:function(a,d,c){function e(e){a.__ko_hasfocusUpdating=m;var f=a.ownerDocument;"activeElement"in
64
- f&&(e=f.activeElement===a);f=d();b.g.ea(f,c,"hasfocus",e,m);a.__ko_hasfocusUpdating=r}var f=e.bind(p,m),g=e.bind(p,r);b.a.n(a,"focus",f);b.a.n(a,"focusin",f);b.a.n(a,"blur",g);b.a.n(a,"focusout",g)},update:function(a,d){var c=b.a.d(d());a.__ko_hasfocusUpdating||(c?a.focus():a.blur(),b.r.K(b.a.Ba,p,[a,c?"focusin":"focusout"]))}};b.c.html={init:function(){return{controlsDescendantBindings:m}},update:function(a,d){b.a.ca(a,d())}};var da="__ko_withIfBindingData";Q("if");Q("ifnot",r,m);Q("with",m,r,function(a,
65
- b){return a.createChildContext(b)});b.c.options={update:function(a,d,c){"select"!==b.a.u(a)&&j(Error("options binding applies only to SELECT elements"));for(var e=0==a.length,f=b.a.V(b.a.fa(a.childNodes,function(a){return a.tagName&&"option"===b.a.u(a)&&a.selected}),function(a){return b.k.q(a)||a.innerText||a.textContent}),g=a.scrollTop,h=b.a.d(d());0<a.length;)b.A(a.options[0]),a.remove(0);if(h){c=c();var k=c.optionsIncludeDestroyed;"number"!=typeof h.length&&(h=[h]);if(c.optionsCaption){var l=y.createElement("option");
66
- b.a.ca(l,c.optionsCaption);b.k.T(l,I);a.appendChild(l)}d=0;for(var n=h.length;d<n;d++){var q=h[d];if(!q||!q._destroy||k){var l=y.createElement("option"),s=function(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c},v=s(q,c.optionsValue,q);b.k.T(l,b.a.d(v));q=s(q,c.optionsText,v);b.a.cb(l,q);a.appendChild(l)}}h=a.getElementsByTagName("option");d=k=0;for(n=h.length;d<n;d++)0<=b.a.i(f,b.k.q(h[d]))&&(b.a.bb(h[d],m),k++);a.scrollTop=g;e&&"value"in c&&ea(a,b.a.ua(c.value),m);b.a.ub(a)}}};
67
- b.c.options.sa="__ko.optionValueDomData__";b.c.selectedOptions={init:function(a,d,c){b.a.n(a,"change",function(){var e=d(),f=[];b.a.o(a.getElementsByTagName("option"),function(a){a.selected&&f.push(b.k.q(a))});b.g.ea(e,c,"value",f)})},update:function(a,d){"select"!=b.a.u(a)&&j(Error("values binding applies only to SELECT elements"));var c=b.a.d(d());c&&"number"==typeof c.length&&b.a.o(a.getElementsByTagName("option"),function(a){var d=0<=b.a.i(c,b.k.q(a));b.a.bb(a,d)})}};b.c.style={update:function(a,
68
- d){var c=b.a.d(d()||{}),e;for(e in c)if("string"==typeof e){var f=b.a.d(c[e]);a.style[e]=f||""}}};b.c.submit={init:function(a,d,c,e){"function"!=typeof d()&&j(Error("The value for a submit binding must be a function"));b.a.n(a,"submit",function(b){var c,h=d();try{c=h.call(e,a)}finally{c!==m&&(b.preventDefault?b.preventDefault():b.returnValue=r)}})}};b.c.text={update:function(a,d){b.a.cb(a,d())}};b.e.I.text=m;b.c.uniqueName={init:function(a,d){if(d()){var c="ko_unique_"+ ++b.c.uniqueName.ob;b.a.ab(a,
69
- c)}}};b.c.uniqueName.ob=0;b.c.value={init:function(a,d,c){function e(){h=r;var e=d(),f=b.k.q(a);b.g.ea(e,c,"value",f)}var f=["change"],g=c().valueUpdate,h=r;g&&("string"==typeof g&&(g=[g]),b.a.P(f,g),f=b.a.Ga(f));if(b.a.Z&&("input"==a.tagName.toLowerCase()&&"text"==a.type&&"off"!=a.autocomplete&&(!a.form||"off"!=a.form.autocomplete))&&-1==b.a.i(f,"propertychange"))b.a.n(a,"propertychange",function(){h=m}),b.a.n(a,"blur",function(){h&&e()});b.a.o(f,function(c){var d=e;b.a.Ob(c,"after")&&(d=function(){setTimeout(e,
70
- 0)},c=c.substring(5));b.a.n(a,c,d)})},update:function(a,d){var c="select"===b.a.u(a),e=b.a.d(d()),f=b.k.q(a),g=e!=f;0===e&&(0!==f&&"0"!==f)&&(g=m);g&&(f=function(){b.k.T(a,e)},f(),c&&setTimeout(f,0));c&&0<a.length&&ea(a,e,r)}};b.c.visible={update:function(a,d){var c=b.a.d(d()),e="none"!=a.style.display;c&&!e?a.style.display="":!c&&e&&(a.style.display="none")}};b.c.click={init:function(a,d,c,e){return b.c.event.init.call(this,a,function(){var a={};a.click=d();return a},c,e)}};b.v=function(){};b.v.prototype.renderTemplateSource=
71
- function(){j(Error("Override renderTemplateSource"))};b.v.prototype.createJavaScriptEvaluatorBlock=function(){j(Error("Override createJavaScriptEvaluatorBlock"))};b.v.prototype.makeTemplateSource=function(a,d){if("string"==typeof a){d=d||y;var c=d.getElementById(a);c||j(Error("Cannot find template with ID "+a));return new b.l.h(c)}if(1==a.nodeType||8==a.nodeType)return new b.l.O(a);j(Error("Unknown template type: "+a))};b.v.prototype.renderTemplate=function(a,b,c,e){a=this.makeTemplateSource(a,e);
72
- return this.renderTemplateSource(a,b,c)};b.v.prototype.isTemplateRewritten=function(a,b){return this.allowTemplateRewriting===r?m:this.makeTemplateSource(a,b).data("isRewritten")};b.v.prototype.rewriteTemplate=function(a,b,c){a=this.makeTemplateSource(a,c);b=b(a.text());a.text(b);a.data("isRewritten",m)};b.b("templateEngine",b.v);var qa=/(<[a-z]+\d*(\s+(?!data-bind=)[a-z0-9\-]+(=(\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind=(["'])([\s\S]*?)\5/gi,ra=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;b.za={vb:function(a,
73
- d,c){d.isTemplateRewritten(a,c)||d.rewriteTemplate(a,function(a){return b.za.Gb(a,d)},c)},Gb:function(a,b){return a.replace(qa,function(a,e,f,g,h,k,l){return W(l,e,b)}).replace(ra,function(a,e){return W(e,"\x3c!-- ko --\x3e",b)})},kb:function(a){return b.s.ra(function(d,c){d.nextSibling&&b.Fa(d.nextSibling,a,c)})}};b.b("__tr_ambtns",b.za.kb);b.l={};b.l.h=function(a){this.h=a};b.l.h.prototype.text=function(){var a=b.a.u(this.h),a="script"===a?"text":"textarea"===a?"value":"innerHTML";if(0==arguments.length)return this.h[a];
74
- var d=arguments[0];"innerHTML"===a?b.a.ca(this.h,d):this.h[a]=d};b.l.h.prototype.data=function(a){if(1===arguments.length)return b.a.f.get(this.h,"templateSourceData_"+a);b.a.f.set(this.h,"templateSourceData_"+a,arguments[1])};b.l.O=function(a){this.h=a};b.l.O.prototype=new b.l.h;b.l.O.prototype.text=function(){if(0==arguments.length){var a=b.a.f.get(this.h,"__ko_anon_template__")||{};a.Aa===I&&a.ia&&(a.Aa=a.ia.innerHTML);return a.Aa}b.a.f.set(this.h,"__ko_anon_template__",{Aa:arguments[0]})};b.l.h.prototype.nodes=
75
- function(){if(0==arguments.length)return(b.a.f.get(this.h,"__ko_anon_template__")||{}).ia;b.a.f.set(this.h,"__ko_anon_template__",{ia:arguments[0]})};b.b("templateSources",b.l);b.b("templateSources.domElement",b.l.h);b.b("templateSources.anonymousTemplate",b.l.O);var O;b.wa=function(a){a!=I&&!(a instanceof b.v)&&j(Error("templateEngine must inherit from ko.templateEngine"));O=a};b.va=function(a,d,c,e,f){c=c||{};(c.templateEngine||O)==I&&j(Error("Set a template engine before calling renderTemplate"));
76
- f=f||"replaceChildren";if(e){var g=N(e);return b.j(function(){var h=d&&d instanceof b.z?d:new b.z(b.a.d(d)),k="function"==typeof a?a(h.$data,h):a,h=T(e,f,k,h,c);"replaceNode"==f&&(e=h,g=N(e))},p,{Ka:function(){return!g||!b.a.X(g)},W:g&&"replaceNode"==f?g.parentNode:g})}return b.s.ra(function(e){b.va(a,d,c,e,"replaceNode")})};b.Mb=function(a,d,c,e,f){function g(a,b){U(b,k);c.afterRender&&c.afterRender(b,a)}function h(d,e){k=f.createChildContext(b.a.d(d),c.as);k.$index=e;var g="function"==typeof a?
77
- a(d,k):a;return T(p,"ignoreTargetNode",g,k,c)}var k;return b.j(function(){var a=b.a.d(d)||[];"undefined"==typeof a.length&&(a=[a]);a=b.a.fa(a,function(a){return c.includeDestroyed||a===I||a===p||!b.a.d(a._destroy)});b.r.K(b.a.$a,p,[e,a,h,c,g])},p,{W:e})};b.c.template={init:function(a,d){var c=b.a.d(d());if("string"!=typeof c&&!c.name&&(1==a.nodeType||8==a.nodeType))c=1==a.nodeType?a.childNodes:b.e.childNodes(a),c=b.a.Hb(c),(new b.l.O(a)).nodes(c);return{controlsDescendantBindings:m}},update:function(a,
78
- d,c,e,f){d=b.a.d(d());c={};e=m;var g,h=p;"string"!=typeof d&&(c=d,d=c.name,"if"in c&&(e=b.a.d(c["if"])),e&&"ifnot"in c&&(e=!b.a.d(c.ifnot)),g=b.a.d(c.data));"foreach"in c?h=b.Mb(d||a,e&&c.foreach||[],c,a,f):e?(f="data"in c?f.createChildContext(g,c.as):f,h=b.va(d||a,f,c,a)):b.e.Y(a);f=h;(g=b.a.f.get(a,"__ko__templateComputedDomDataKey__"))&&"function"==typeof g.B&&g.B();b.a.f.set(a,"__ko__templateComputedDomDataKey__",f&&f.pa()?f:I)}};b.g.Q.template=function(a){a=b.g.aa(a);return 1==a.length&&a[0].unknown||
79
- b.g.Eb(a,"name")?p:"This template engine does not support anonymous templates nested within its templates"};b.e.I.template=m;b.b("setTemplateEngine",b.wa);b.b("renderTemplate",b.va);b.a.Ja=function(a,b,c){a=a||[];b=b||[];return a.length<=b.length?S(a,b,"added","deleted",c):S(b,a,"deleted","added",c)};b.b("utils.compareArrays",b.a.Ja);b.a.$a=function(a,d,c,e,f){function g(a,b){t=l[b];w!==b&&(z[a]=t);t.na(w++);M(t.M);s.push(t);A.push(t)}function h(a,c){if(a)for(var d=0,e=c.length;d<e;d++)c[d]&&b.a.o(c[d].M,
80
- function(b){a(b,d,c[d].U)})}d=d||[];e=e||{};var k=b.a.f.get(a,"setDomNodeChildrenFromArrayMapping_lastMappingResult")===I,l=b.a.f.get(a,"setDomNodeChildrenFromArrayMapping_lastMappingResult")||[],n=b.a.V(l,function(a){return a.U}),q=b.a.Ja(n,d),s=[],v=0,w=0,B=[],A=[];d=[];for(var z=[],n=[],t,D=0,C,E;C=q[D];D++)switch(E=C.moved,C.status){case "deleted":E===I&&(t=l[v],t.j&&t.j.B(),B.push.apply(B,M(t.M)),e.beforeRemove&&(d[D]=t,A.push(t)));v++;break;case "retained":g(D,v++);break;case "added":E!==I?
81
- g(D,E):(t={U:C.value,na:b.m(w++)},s.push(t),A.push(t),k||(n[D]=t))}h(e.beforeMove,z);b.a.o(B,e.beforeRemove?b.A:b.removeNode);for(var D=0,k=b.e.firstChild(a),H;t=A[D];D++){t.M||b.a.extend(t,ha(a,c,t.U,f,t.na));for(v=0;q=t.M[v];k=q.nextSibling,H=q,v++)q!==k&&b.e.Pa(a,q,H);!t.Ab&&f&&(f(t.U,t.M,t.na),t.Ab=m)}h(e.beforeRemove,d);h(e.afterMove,z);h(e.afterAdd,n);b.a.f.set(a,"setDomNodeChildrenFromArrayMapping_lastMappingResult",s)};b.b("utils.setDomNodeChildrenFromArrayMapping",b.a.$a);b.C=function(){this.allowTemplateRewriting=
82
- r};b.C.prototype=new b.v;b.C.prototype.renderTemplateSource=function(a){var d=!(9>b.a.Z)&&a.nodes?a.nodes():p;if(d)return b.a.L(d.cloneNode(m).childNodes);a=a.text();return b.a.ta(a)};b.C.oa=new b.C;b.wa(b.C.oa);b.b("nativeTemplateEngine",b.C);b.qa=function(){var a=this.Db=function(){if("undefined"==typeof F||!F.tmpl)return 0;try{if(0<=F.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,c,e){e=e||{};2>a&&j(Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later."));
83
- var f=b.data("precompiled");f||(f=b.text()||"",f=F.template(p,"{{ko_with $item.koBindingContext}}"+f+"{{/ko_with}}"),b.data("precompiled",f));b=[c.$data];c=F.extend({koBindingContext:c},e.templateOptions);c=F.tmpl(f,b,c);c.appendTo(y.createElement("div"));F.fragments={};return c};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){y.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(F.tmpl.tag.ko_code=
84
- {open:"__.push($1 || '');"},F.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};b.qa.prototype=new b.v;w=new b.qa;0<w.Db&&b.wa(w);b.b("jqueryTmplTemplateEngine",b.qa)}"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?L(module.exports||exports):"function"===typeof define&&define.amd?define(["exports"],L):L(x.ko={});m;
85
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
uninstall.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // if uninstall not called from WordPress exit
4
+ if ( !defined( 'WP_UNINSTALL_PLUGIN' ) ) exit();
5
+
6
+ require('wp-security-audit-log.php');
7
+ WpSecurityAuditLog::GetInstance()->Uninstall();
wp-security-audit-log.php CHANGED
@@ -4,14 +4,12 @@ Plugin Name: WP Security Audit Log
4
  Plugin URI: http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/
5
  Description: Identify WordPress security issues before they become a problem and keep track of everything happening on your WordPress, including WordPress users activity. Similar to Windows Event Log and Linux Syslog, WP Security Audit Log will generate a security alert for everything that happens on your WordPress blog or website. Use the Audit Log Viewer included in the plugin to see all the security alerts.
6
  Author: WP White Security
7
- Version: 0.6.3
8
  Author URI: http://www.wpwhitesecurity.com/
9
  License: GPL2
10
- Text Domain: wp-security-audit-log
11
- Domain Path: /languages
12
 
13
  WP Security Audit Log
14
- Copyright(c) 2013 Robert Abela (email : robert@wpwhitesecurity.com)
15
 
16
  This program is free software; you can redistribute it and/or modify
17
  it under the terms of the GNU General Public License, version 2, as
@@ -26,183 +24,224 @@ Domain Path: /languages
26
  along with this program; if not, write to the Free Software
27
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28
  */
29
- define('WPPH_PLUGIN_VERSION','0.6.3');
30
- define('WPPH_PLUGIN_PREFIX', 'wpph_');
31
- define('WPPH_PLUGIN_NAME', 'WP Security Audit Log');
32
- define('WPPH_PLUGIN_URL', trailingslashit(plugins_url('', __FILE__)));
33
- define('WPPH_PLUGIN_DIR', trailingslashit(plugin_dir_path(__FILE__)));
34
- if(defined('__DIR__')) { define('WPPH_PLUGIN_BASE_NAME', basename(__DIR__)); }
35
- else { define('WPPH_PLUGIN_BASE_NAME', basename(dirname(__FILE__))); }
36
 
37
- // Load required files
38
- require('inc/wpphSettings.php');
39
- require('inc/WPPH.php');
40
- require('inc/WPPHLogger.php');
41
- require('inc/WPPHUtil.php');
42
- require('inc/WPPHNetwork.php');
43
- require('inc/WPPHDatabase.php');
44
- require('inc/WPPHEvent.php');
45
- require('inc/WPPHPost.php');
46
- require('inc/wpphFunctions.php');
47
-
48
-
49
- //#[ENABLE LOGGING]#
50
- //===============================================
51
- // WPPHLogger::enableDebugLogging();
52
- // WPPHLogger::enableErrorLogging();
53
- //===============================================
54
-
55
- //# 6000
56
- function wpph_addCronIntervals( $schedules )
57
- {
58
- $schedules['8h'] = array('interval' => 28800,'display' => __('Every 8 hours',WPPH_PLUGIN_TEXT_DOMAIN));
59
- return $schedules;
60
- }
61
- define('WPPH_CLEANUP_INTERVAL','8h');
62
- // must be in seconds, the time interval when the wp cron runs
63
- define('WPPH_CLEANUP_WAIT_TIME', strtotime(28800));
64
- add_filter('cron_schedules', 'wpph_addCronIntervals');
65
-
66
-
67
- // load the text domain
68
- add_action('plugins_loaded', array('WPPH','loadTextDomain'));
69
-
70
-
71
- // 2000
72
- $GLOBALS['WPPH_POST_IS_NEW'] = false;
73
- add_action('wp_insert_post', 'wpphPostDetectNew', 1, 2);
74
- function wpphPostDetectNew($post, $wp_error = false){
75
- wpphLog(__FUNCTION__.' triggered by hook: WP_INSERT_POST');
76
- if(isset($_POST['post_id'])){
77
- $GLOBALS['WPPH_POST_IS_NEW'] = true;
78
- wpphLog('POST IS NEW');
79
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
81
 
82
- // register callbacks
83
- if (WPPH::isMultisite()) { add_action('network_admin_menu', array('WPPH','createPluginWpSidebar')); }
84
- else add_action('admin_menu', array('WPPH','createPluginWpSidebar'));
85
- register_activation_hook( __FILE__, array('WPPHDatabase','dropTables')); // since v0.6
86
- register_activation_hook( __FILE__, array('wpphNetwork','networkActivate'));
87
- register_deactivation_hook( __FILE__, array('wpphNetwork','networkDeactivate'));
88
- register_uninstall_hook( __FILE__, array('wpphNetwork','networkUninstall'));
89
-
90
- // Add custom links on plugins page
91
- add_filter("plugin_action_links_".plugin_basename(__FILE__), 'wpphCustomLinks' );
92
- // Load text domain
93
- add_action('plugins_loaded', 'wpphLoadTextDomain');
94
- // create dashboard widget
95
- add_action('wp_dashboard_setup', array('WPPHUtil','addDashboardWidget'));
96
- // activate plugin for new created websites
97
- add_action('wpmu_new_blog', array('WPPH','onPluginActivate'));
98
-
99
- // $GLOBALS['WPPH_CAN_RUN']
100
- // @since v0.3
101
- // @see WPPH::onPluginActivate()
102
- if($GLOBALS['WPPH_CAN_RUN'])
103
- {
104
- // Watch widget activity
105
- add_action('widgets_init',array('WPPHEventWatcher','watchWidgetMove'));
106
- add_action('sidebar_admin_setup', array('WPPHEventWatcher','watchWidgetActivity'));
107
- // Load the pluggable.php file if needed
108
- add_action('admin_init', array('WPPHUtil','loadPluggable'));
109
- // Load resources
110
- add_action('admin_init', array('WPPH','loadBaseResources'));
111
- // Add the sidebar menu
112
- add_action('admin_menu', array('WPPH','createPluginWpSidebar'));
113
-
114
- if(WPPH::isMultisite())
115
- {
116
- // 4001 && 4002 - wpmu user activated/registered
117
- add_action('wpmu_activate_user', array('WPPHEventWatcher', 'watchEventUserRegister'));
118
- // 4008 && 4009
119
- WPPHEvent::hookUserAdminPriv();
120
- // 4010 - user added to blog
121
- add_action('add_user_to_blog', array('WPPHEventWatcher','watchUserAddedToBlog'), 10, 3);
122
- // 4011 - user removed from blog
123
- add_action('remove_user_from_blog', array('WPPHEventWatcher','watchUserRemovedFromBlog'));
124
- // 7000 - new blog added
125
- add_action('wpmu_new_blog', array('WPPHEventWatcher','watchBlogAdded'), 10, 6);
126
- // 7001 - blog archived
127
- add_action('archive_blog', array('WPPHEventWatcher','watchBlogArchived'));
128
- // 7002 - blog unarchived
129
- add_action('unarchive_blog', array('WPPHEventWatcher','watchBlogUnarchived'));
130
- // 7003 - blog activated
131
- add_action('activate_blog', array('WPPHEventWatcher','watchBlogActivated'));
132
- // 7004 - blog deactivated
133
- add_action('deactivate_blog', array('WPPHEventWatcher','watchBlogDeactivated'));
134
- // 7005 - blog deleted
135
- add_action('delete_blog', array('WPPHEventWatcher','watchBlogDeleted'));
136
- }
137
- // end wpmu events
138
 
139
- WPPHEvent::hookLoginEvent();
140
- WPPHEvent::hookLogoutEvent();
141
- WPPHEvent::hookLoginFailure();
142
- // 4000 && 4001 && 4012
143
- WPPHEvent::hookUserRegisterEvent();
144
- // 4003 && 4004
145
- WPPHEvent::hookUserPasswordUpdated();
146
- // 4005 && 4006
147
- WPPHEvent::hookUserEmailUpdated();
148
-
149
- // 6000
150
- if(defined('DISABLE_WP_CRON') && DISABLE_WP_CRON)
151
- {
152
- wpphLog('wp-cron is disabled.');
153
- WPPHEventWatcher::__deleteEvents();
154
- }
155
- else { add_action(WPPH_PLUGIN_DEL_EVENTS_CRON_TASK_NAME, array('WPPHEventWatcher','__deleteEvents')); }
156
-
157
- // Plugin init
158
- add_action('init', 'wpphPluginInit');
159
- function wpphPluginInit()
160
- {
161
- if(is_admin())
162
- {
163
- if(isset($_POST)){
164
- //# 6001, 6002, 6003
165
- WPPHEvent::hookCheckWpGeneralSettings();
166
- if(isset($_POST)){
167
- if(isset($_POST['action']) && $_POST['action'] == 'editpost'){
168
- wpphLog('WPPH_DEFAULT_EDITOR_ENABLED');
169
- $GLOBALS['WPPH_DEFAULT_EDITOR_ENABLED'] = true;
170
- }
171
- elseif(isset($_POST['screen'])){
172
- if($_POST['screen'] == 'edit-post' || $_POST['screen'] == 'edit-page'){
173
- wpphLog('WPPH_SCREEN_EDITOR_ENABLED');
174
- $GLOBALS['WPPH_SCREEN_EDITOR_ENABLED'] = true;
175
- }
176
- else {// Custom Post type screen
177
- $type = wpph_extractCustomPostType($_POST['screen']);
178
- if(WPPHPost::validatePostType($type)){
179
- wpphLog('WPPH_SCREEN_EDITOR_ENABLED');
180
- $GLOBALS['WPPH_SCREEN_EDITOR_ENABLED'] = true;
181
- }
182
- }
183
- }
184
- }
185
- }
186
- WPPHEventWatcher::triggerWidgetMoveEvent();
187
- WPPHEvent::hookWatchPostStateBefore();
188
- WPPHEvent::hookWatchBlogActivity();
189
- WPPHEvent::hookWatchCategoryAdd();
190
- WPPHEvent::hookWatchCategoryDelete();
191
- WPPHEvent::hookFileDeletion();
192
- WPPHEvent::hookFileUploaded();
193
- WPPHEvent::hookFileUploadedDeleted();
194
- WPPHEvent::hookTrashPost();
195
- WPPHEvent::hookTrashPage();
196
- WPPHEvent::hookUntrashedPosts();
197
- WPPHEvent::hookUntrashedPages();
198
- WPPHEvent::hookThemeChange();
199
- WPPHEvent::hookUserDeletion();
200
- WPPHEvent::hookWatchPluginActivity();
201
- /* Enable ajax functionality in the dashboard page */
202
- add_action('wp_ajax_wpph_get_events', array('WPPHUtil','get_events_html'));
203
- add_action('wp_ajax_wpph_get_blogs', array('WPPHUtil','ajax_get_blogs'));
204
- add_action('wp_ajax_wpph_check_user_role', array('WPPHUtil','check_user_role'));
205
- }
206
- }
207
- }
208
- // End wp-security-audit-log
4
  Plugin URI: http://www.wpwhitesecurity.com/wordpress-security-plugins/wp-security-audit-log/
5
  Description: Identify WordPress security issues before they become a problem and keep track of everything happening on your WordPress, including WordPress users activity. Similar to Windows Event Log and Linux Syslog, WP Security Audit Log will generate a security alert for everything that happens on your WordPress blog or website. Use the Audit Log Viewer included in the plugin to see all the security alerts.
6
  Author: WP White Security
7
+ Version: 1.0.0
8
  Author URI: http://www.wpwhitesecurity.com/
9
  License: GPL2
 
 
10
 
11
  WP Security Audit Log
12
+ Copyright(c) 2014 Robert Abela (email : robert@wpwhitesecurity.com)
13
 
14
  This program is free software; you can redistribute it and/or modify
15
  it under the terms of the GNU General Public License, version 2, as
24
  along with this program; if not, write to the Free Software
25
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
  */
 
 
 
 
 
 
 
27
 
28
+ class WpSecurityAuditLog {
29
+
30
+ // <editor-fold desc="Properties & Constants">
31
+
32
+ const PLG_CLS_PRFX = 'WSAL_';
33
+
34
+ /**
35
+ * Views supervisor.
36
+ * @var WSAL_ViewManager
37
+ */
38
+ public $views;
39
+
40
+ /**
41
+ * Logger supervisor.
42
+ * @var WSAL_AlertManager
43
+ */
44
+ public $alerts;
45
+
46
+ /**
47
+ * Sensors supervisor.
48
+ * @var WSAL_SensorManager
49
+ */
50
+ public $sensors;
51
+
52
+ /**
53
+ * Settings manager.
54
+ * @var WSAL_Settings
55
+ */
56
+ public $settings;
57
+
58
+ /**
59
+ * Constants manager.
60
+ * @var WSAL_ConstantManager
61
+ */
62
+ public $constants;
63
+
64
+ /**
65
+ * Contains a list of cleanup callbacks.
66
+ * @var callable[]
67
+ */
68
+ protected $_cleanup_hooks = array();
69
+
70
+ // </editor-fold>
71
+
72
+ // <editor-fold desc="Entry Points">
73
+
74
+ /**
75
+ * Standard singleton pattern.
76
+ * @return \self Returns the current plugin instance.
77
+ */
78
+ public static function GetInstance(){
79
+ static $instance = null;
80
+ if(!$instance)$instance = new self();
81
+ return $instance;
82
+ }
83
+
84
+ /**
85
+ * Initialize plugin.
86
+ */
87
+ public function __construct(){
88
+ spl_autoload_register(array($this, 'LoadClass'));
89
+
90
+ // load dependencies
91
+ $this->views = new WSAL_ViewManager($this);
92
+ $this->alerts = new WSAL_AlertManager($this);
93
+ $this->sensors = new WSAL_SensorManager($this);
94
+ $this->settings = new WSAL_Settings($this);
95
+ $this->constants = new WSAL_ConstantManager($this);
96
+ $this->widgets = new WSAL_WidgetManager($this);
97
+
98
+ // listen to general events
99
+ $this->sensors->HookEvents();
100
+
101
+ // listen for installation event
102
+ register_activation_hook(__FILE__, array($this, 'Install'));
103
+
104
+ // listen for cleanup event
105
+ add_action('wsal_cleanup', array($this, 'CleanUp'));
106
+ //add_action('init', array($this, 'CleanUp'));
107
+ }
108
+
109
+ public function CleanUp(){
110
+ foreach($this->_cleanup_hooks as $hook)
111
+ call_user_func($hook);
112
+ }
113
+
114
+ public function AddCleanupHook($hook){
115
+ $this->_cleanup_hooks[] = $hook;
116
+ }
117
+
118
+ public function RemoveCleanupHook($hook){
119
+ while(($pos = array_search($hook, $this->_cleanup_hooks)) !== false)
120
+ unset($this->_cleanup_hooks[$pos]);
121
+ }
122
+
123
+ public function Install(){
124
+ WSAL_DB_ActiveRecord::InstallAll();
125
+ if($this->CanUpgrade())$this->Upgrade();
126
+ wp_schedule_event(0, 'hourly', 'wsal_cleanup');
127
+ }
128
+
129
+ public function Uninstall(){
130
+ WSAL_DB_ActiveRecord::UninstallAll();
131
+ wp_unschedule_event(0, 'wsal_cleanup');
132
+ }
133
+
134
+ public function CanUpgrade(){
135
+ global $wpdb;
136
+ $table = $wpdb->base_prefix . 'wordpress_auditlog_events';
137
+ return $wpdb->get_var('SHOW TABLES LIKE "'.$table.'"') == $table;
138
+ }
139
+
140
+ public function Upgrade(){
141
+ global $wpdb;
142
+ static $migTypes = array(
143
+ 3000 => 5006
144
+ );
145
+
146
+ // load data
147
+ $sql = 'SELECT * FROM ' . $wpdb->base_prefix . 'wordpress_auditlog_events';
148
+ $events = array();
149
+ foreach($wpdb->get_results($sql, ARRAY_A) as $item)
150
+ $events[$item['EventID']] = $item;
151
+ $sql = 'SELECT * FROM ' . $wpdb->base_prefix . 'wordpress_auditlog';
152
+ $auditlog = $wpdb->get_results($sql, ARRAY_A);
153
+
154
+ // migrate using db logger
155
+ $lgr = new WSAL_Loggers_Database($this);
156
+ foreach($auditlog as $entry){
157
+ $data = array(
158
+ 'ClientIP' => $entry['UserIP'],
159
+ 'UserAgent' => '',
160
+ 'CurrentUserID' => $entry['UserID'],
161
+ );
162
+ if($entry['UserName'])
163
+ $data['Username'] = base64_decode($entry['UserName']);
164
+ $mesg = $events[$entry['EventID']]['EventDescription'];
165
+ $date = strtotime($entry['EventDate']);
166
+ $type = $entry['EventID'];
167
+ if(isset($migTypes[$type]))$type = $migTypes[$type];
168
+ // convert message from '<strong>%s</strong>' to '%Arg1%' format
169
+ $c = 0; $n = '<strong>%s</strong>'; $l = strlen($n);
170
+ while(($pos = strpos($mesg, $n)) !== false){
171
+ $mesg = substr_replace($mesg, '%MigratedArg' . ($c++) .'%', $pos, $l);
172
+ }
173
+ $data['MigratedMesg'] = $mesg;
174
+ // generate new meta data args
175
+ $temp = unserialize(base64_decode($entry['EventData']));
176
+ foreach((array)$temp as $i => $item)
177
+ $data['MigratedArg' . $i] = $item;
178
+ // send event data to logger!
179
+ $lgr->Log($type, $data, $date, $entry['BlogId'], true);
180
+ }
181
+
182
+ // migrate settings
183
+ $this->settings->SetAllowedPluginEditors(
184
+ get_option('WPPH_PLUGIN_ALLOW_CHANGE')
185
+ );
186
+ $this->settings->SetAllowedPluginViewers(
187
+ get_option('WPPH_PLUGIN_ALLOW_ACCESS')
188
+ );
189
+ $s = get_option('wpph_plugin_settings');
190
+ //$this->settings->SetPruningDate(($s->daysToKeep ? $s->daysToKeep : 30) . ' days');
191
+ //$this->settings->SetPruningLimit(min($s->eventsToKeep, 1));
192
+ $this->settings->SetViewPerPage(max($s->showEventsViewList, 5));
193
+ $this->settings->SetWidgetsEnabled(!!$s->showDW);
194
+ }
195
+
196
+ public function GetBaseUrl(){
197
+ return plugins_url('', __FILE__);
198
+ }
199
+
200
+ public function GetBaseDir(){
201
+ return plugin_dir_path(__FILE__);
202
+ }
203
+
204
+ public function GetBaseName(){
205
+ return plugin_basename(__FILE__);
206
+ }
207
+
208
+ // </editor-fold>
209
+
210
+ /**
211
+ * This is the class autoloader. You should not call this directly.
212
+ * @param string $class Class name.
213
+ * @return boolean True if class is found and loaded, false otherwise.
214
+ */
215
+ public function LoadClass($class){
216
+ if(substr($class, 0, strlen(self::PLG_CLS_PRFX)) == self::PLG_CLS_PRFX){
217
+ $file = str_replace('_', DIRECTORY_SEPARATOR, substr($class, strlen(self::PLG_CLS_PRFX)));
218
+ $file = $this->GetBaseDir() . 'classes' . DIRECTORY_SEPARATOR . $file . '.php';
219
+ if(file_exists($file)){
220
+ require_once($file);
221
+ return class_exists($class, false) || interface_exists($class, false);
222
+ }
223
+ }
224
+ return false;
225
+ }
226
+
227
+ /**
228
+ * Returns the class name of a particular file that contains the class.
229
+ * @param string $file File name.
230
+ * @return string Class name.
231
+ */
232
+ public function GetClassFileClassName($file){
233
+ $base = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $this->GetBaseDir() . 'classes' . DIRECTORY_SEPARATOR);
234
+ $file = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $file);
235
+ return str_replace(
236
+ array($base, '\\', '/'),
237
+ array(self::PLG_CLS_PRFX, '_', '_'),
238
+ substr($file, 0, -4)
239
+ );
240
+ }
241
  }
242
 
243
+ // Load extra files
244
+ require_once('defaults.php');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
+ // Create & Run the plugin
247
+ return WpSecurityAuditLog::GetInstance();