WP Security Audit Log - Version 1.2.7

Version Description

(2014-09-26) = * New Feature * New option "Restrict Plugin Access" that allows WordPress administrators to further restrict access to the plugin and the WordPress security alerts

  • Improvements

    • Updated the Audit Log Viewer backend to retriev WordPress security alerts much faster and consume less resources on large websites
    • Moved the Audit Log plugin menu entry underneath the dashboard entry for better access
    • Several minor enhancements to the plugin to perform better on large WordPress installations
  • Bug Fixes

    • Fixed an uncaught exception with Logout Alert 1001 support ticket
Download this release

Release Info

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

Code changes from version 1.2.6 to 1.2.7

classes/AbstractView.php CHANGED
@@ -59,6 +59,27 @@ abstract class WSAL_AbstractView {
59
  */
60
  abstract public function Render();
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  /**
63
  * @return boolean Whether page should appear in menu or not.
64
  */
59
  */
60
  abstract public function Render();
61
 
62
+ /**
63
+ * Renders the view icon (this has been deprecated in newwer WP versions).
64
+ */
65
+ public function RenderIcon(){
66
+ ?><div id="icon-plugins" class="icon32"><br></div><?php
67
+ }
68
+
69
+ /**
70
+ * Renders the view title.
71
+ */
72
+ public function RenderTitle(){
73
+ ?><h2><?php echo esc_html($this->GetTitle()); ?></h2><?php
74
+ }
75
+
76
+ /**
77
+ * @link self::Render()
78
+ */
79
+ public function RenderContent(){
80
+ $this->Render();
81
+ }
82
+
83
  /**
84
  * @return boolean Whether page should appear in menu or not.
85
  */
classes/AlertManager.php CHANGED
@@ -95,13 +95,17 @@ final class WSAL_AlertManager {
95
  /**
96
  * @internal Commit an alert now.
97
  */
98
- protected function _CommitItem($type, $data, $cond){
99
  if(!$cond || !!call_user_func($cond, $this)){
100
  if($this->IsEnabled($type)){
101
  if(isset($this->_alerts[$type])){
102
  // ok, convert alert to a log entry
103
  $this->_triggered_types[] = $type;
104
  $this->Log($type, $data);
 
 
 
 
105
  }else{
106
  // in general this shouldn't happen, but it could, so we handle it here :)
107
  throw new Exception('Alert with code "' . $type . '" has not be registered.');
@@ -215,7 +219,7 @@ final class WSAL_AlertManager {
215
  if(!isset($data['Username']) && !isset($data['CurrentUserID']))
216
  $data['CurrentUserID'] = function_exists('get_current_user_id') ? get_current_user_id() : 0;
217
  if(!isset($data['CurrentUserRoles']) && function_exists('is_user_logged_in') && is_user_logged_in())
218
- $data['CurrentUserRoles'] = wp_get_current_user()->roles;
219
 
220
  //if(isset($_SERVER['REMOTE_HOST']) && $_SERVER['REMOTE_HOST'] != $data['ClientIP'])
221
  // $data['ClientHost'] = $_SERVER['REMOTE_HOST'];
95
  /**
96
  * @internal Commit an alert now.
97
  */
98
+ protected function _CommitItem($type, $data, $cond, $_retry = true){
99
  if(!$cond || !!call_user_func($cond, $this)){
100
  if($this->IsEnabled($type)){
101
  if(isset($this->_alerts[$type])){
102
  // ok, convert alert to a log entry
103
  $this->_triggered_types[] = $type;
104
  $this->Log($type, $data);
105
+ }elseif($_retry){
106
+ // this is the last attempt at loading alerts from default file
107
+ $this->plugin->LoadDefaults();
108
+ return $this->_CommitItem($type, $data, $cond, false);
109
  }else{
110
  // in general this shouldn't happen, but it could, so we handle it here :)
111
  throw new Exception('Alert with code "' . $type . '" has not be registered.');
219
  if(!isset($data['Username']) && !isset($data['CurrentUserID']))
220
  $data['CurrentUserID'] = function_exists('get_current_user_id') ? get_current_user_id() : 0;
221
  if(!isset($data['CurrentUserRoles']) && function_exists('is_user_logged_in') && is_user_logged_in())
222
+ $data['CurrentUserRoles'] = $this->plugin->settings->GetCurrentUserRoles();
223
 
224
  //if(isset($_SERVER['REMOTE_HOST']) && $_SERVER['REMOTE_HOST'] != $data['ClientIP'])
225
  // $data['ClientHost'] = $_SERVER['REMOTE_HOST'];
classes/AuditLogListView.php ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(ABSPATH . 'wp-admin/includes/admin.php');
4
+ require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
5
+
6
+ class WSAL_AuditLogListView extends WP_List_Table {
7
+
8
+ /**
9
+ * @var WpSecurityAuditLog
10
+ */
11
+ protected $_plugin;
12
+
13
+ public function __construct($plugin){
14
+ $this->_plugin = $plugin;
15
+
16
+ $this->_gmt_offset_sec = get_option('gmt_offset') * HOUR_IN_SECONDS;
17
+
18
+ parent::__construct(array(
19
+ 'singular' => 'log',
20
+ 'plural' => 'logs',
21
+ 'ajax' => true,
22
+ 'screen' => 'interval-list',
23
+ ));
24
+ }
25
+
26
+ protected $_gmt_offset_sec = 0;
27
+
28
+ public function no_items(){
29
+ _e('No events so far.', 'wp-security-audit-log');
30
+ }
31
+
32
+ public function extra_tablenav($which){
33
+ // items-per-page widget
34
+ $o = __('Other', 'wp-security-audit-log');
35
+ $p = $this->_plugin->settings->GetViewPerPage();
36
+ $items = array($o, 5, 10, 15, 30, 50);
37
+ if (!in_array($p, $items)) $items[] = $p;
38
+ if ($p == $o || $p == 0) $p = $o[1]; // a sane default if things goes bust
39
+
40
+ ?><div class="wsal-ipp wsal-ipp-<?php echo $which; ?>">
41
+ <?php _e('Show ', 'wp-security-audit-log'); ?>
42
+ <select class="wsal-ipps" onfocus="WsalIppsFocus(value);" onchange="WsalIppsChange(value);">
43
+ <?php foreach($items as $item){ ?>
44
+ <option
45
+ value="<?php echo is_string($item) ? '' : $item; ?>"
46
+ <?php if($item == $p)echo 'selected="selected"'; ?>><?php
47
+ echo $item;
48
+ ?></option>
49
+ <?php } ?>
50
+ </select>
51
+ <?php _e(' Items', 'wp-security-audit-log'); ?>
52
+ </div><?php
53
+
54
+ // show site alerts widget
55
+ if($this->is_multisite() && $this->is_main_blog()){
56
+ $curr = $this->get_view_site_id();
57
+ ?><div class="wsal-ssa wsal-ssa-<?php echo $which; ?>">
58
+ <?php if($this->get_site_count() > 15){ ?>
59
+ <?php $curr = $curr ? get_blog_details($curr) : null; ?>
60
+ <?php $curr = $curr ? ($curr->blogname . ' (' . $curr->domain . ')') : 'All Sites'; ?>
61
+ <input type="text" class="wsal-ssas" value="<?php echo esc_attr($curr); ?>"/>
62
+ <?php }else{ ?>
63
+ <select class="wsal-ssas" onchange="WsalSsasChange(value);">
64
+ <option value="0"><?php _e('All Sites', 'wp-security-audit-log'); ?></option>
65
+ <?php foreach($this->get_sites() as $info){ ?>
66
+ <option value="<?php echo $info->blog_id; ?>"
67
+ <?php if($info->blog_id == $curr)echo 'selected="selected"'; ?>><?php
68
+ echo esc_html($info->blogname) . ' (' . esc_html($info->domain) . ')';
69
+ ?></option>
70
+ <?php } ?>
71
+ </select>
72
+ <?php } ?>
73
+ </div><?php
74
+ }
75
+ }
76
+
77
+ /**
78
+ * @param int|null $limit Maximum number of sites to return (null = no limit).
79
+ * @return object Object with keys: blog_id, blogname, domain
80
+ */
81
+ public function get_sites($limit = null){
82
+ global $wpdb;
83
+
84
+ // build query
85
+ $sql = 'SELECT blog_id, domain FROM ' . $wpdb->blogs;
86
+ if(!is_null($limit))$sql .= ' LIMIT ' . $limit;
87
+
88
+ // execute query
89
+ $res = $wpdb->get_results($sql);
90
+
91
+ // modify result
92
+ foreach($res as $row){
93
+ $row->blogname = get_blog_option($row->blog_id, 'blogname');
94
+ }
95
+
96
+ // return result
97
+ return $res;
98
+ }
99
+
100
+ /**
101
+ * @return int The number of sites on the network.
102
+ */
103
+ public function get_site_count(){
104
+ global $wpdb;
105
+ $sql = 'SELECT COUNT(*) FROM ' . $wpdb->blogs;
106
+ return (int)$wpdb->get_var($sql);
107
+ }
108
+
109
+ public function get_columns(){
110
+ $cols = array(
111
+ //'cb' => '<input type="checkbox" />',
112
+ //'read' => __('Read', 'wp-security-audit-log'),
113
+ 'type' => __('Code', 'wp-security-audit-log'),
114
+ 'code' => __('Type', 'wp-security-audit-log'),
115
+ 'crtd' => __('Date', 'wp-security-audit-log'),
116
+ 'user' => __('Username', 'wp-security-audit-log'),
117
+ 'scip' => __('Source IP', 'wp-security-audit-log'),
118
+ );
119
+ if($this->is_multisite() && $this->is_main_blog() && !$this->is_specific_view()){
120
+ $cols['site'] = __('Site', 'wp-security-audit-log');
121
+ }
122
+ $cols['mesg'] = __('Message', 'wp-security-audit-log');
123
+ if($this->_plugin->settings->IsDataInspectorEnabled()){
124
+ $cols['data'] = '';
125
+ }
126
+ return $cols;
127
+ }
128
+
129
+ public function column_cb(WSAL_DB_Occurrence $item){
130
+ return '<input type="checkbox" value="'.$item->id.'" '
131
+ . 'name="'.esc_attr($this->_args['singular']).'[]"/>';
132
+ }
133
+
134
+ public function get_sortable_columns(){
135
+ return array(
136
+ 'read' => array('is_read', false),
137
+ 'code' => array('code', false),
138
+ 'type' => array('alert_id', false),
139
+ 'crtd' => array('created_on', true),
140
+ 'user' => array('user', false),
141
+ 'scip' => array('scip', false),
142
+ 'site' => array('site', false),
143
+ );
144
+ }
145
+
146
+ public function column_default(WSAL_DB_Occurrence $item, $column_name){
147
+ switch($column_name){
148
+ case 'read':
149
+ return '<span class="log-read log-read-'
150
+ . ($item->is_read ? 'old' : 'new')
151
+ . '" title="' . __('Click to toggle.', 'wp-security-audit-log') . '"></span>';
152
+ case 'type':
153
+ return str_pad($item->alert_id, 4, '0', STR_PAD_LEFT);
154
+ case 'code':
155
+ $code = $this->_plugin->alerts->GetAlert($item->alert_id);
156
+ $code = $code ? $code->code : 0;
157
+ $const = (object)array('name' => 'E_UNKNOWN', 'value' => 0, 'description' => __('Unknown error code.', 'wp-security-audit-log'));
158
+ $const = $this->_plugin->constants->GetConstantBy('value', $code, $const);
159
+ return '<span class="log-type log-type-' . $const->value
160
+ . '" title="' . esc_html($const->name . ': ' . $const->description) . '"></span>';
161
+ case 'crtd':
162
+ return $item->created_on ? (
163
+ str_replace(
164
+ '$$$',
165
+ substr(number_format(fmod($item->created_on + $this->_gmt_offset_sec, 1), 3), 2),
166
+ date('Y-m-d<\b\r>h:i:s.$$$&\n\b\s\p;A', $item->created_on + $this->_gmt_offset_sec)
167
+ )
168
+ ) : '<i>unknown</i>';
169
+ case 'user':
170
+ $username = $item->GetUsername();
171
+ if($username && ($user = get_user_by('login', $username))){
172
+ $image = get_avatar($user->ID, 32);
173
+ $uhtml = '<a href="' . admin_url('user-edit.php?user_id=' . $user->ID)
174
+ . '" target="_blank">' . esc_html($user->display_name) . '</a>';
175
+ $roles = $item->GetUserRoles();
176
+ $roles = (is_array($roles) && count($roles))
177
+ ? __(esc_html(ucwords(implode(', ', $roles))))
178
+ : '<i>' . __('Unknown', 'wp-security-audit-log') . '</i>';
179
+ }else{
180
+ $image = get_avatar(0, 32);
181
+ $uhtml = '<i>' . __('Unknown', 'wp-security-audit-log') . '</i>';
182
+ $roles = '<i>' . __('System', 'wp-security-audit-log') . '</i>';
183
+ }
184
+ return $image . $uhtml . '<br/>' . $roles;
185
+ case 'scip':
186
+ return !is_null($item->GetSourceIP()) ? esc_html($item->GetSourceIP()) : '<i>unknown</i>';
187
+ case 'site':
188
+ $info = get_blog_details($item->site_id, true);
189
+ return !$info ? ('Unknown Site '.$item->site_id)
190
+ : ('<a href="' . esc_attr($info->siteurl) . '">' . esc_html($info->blogname) . '</a>');
191
+ case 'mesg':
192
+ return '<div id="Event' . $item->id . '">' . $item->GetMessage(array($this, 'meta_formatter')) . '</div>';
193
+ case 'data':
194
+ $url = admin_url('admin-ajax.php') . '?action=AjaxInspector&amp;occurrence=' . $item->id;
195
+ return '<a class="more-info thickbox" title="' . __('Alert Data Inspector', 'wp-security-audit-log') . '"'
196
+ . ' href="' . $url . '&amp;TB_iframe=true&amp;width=600&amp;height=550">&hellip;</a>';
197
+ default:
198
+ return isset($item->$column_name)
199
+ ? esc_html($item->$column_name)
200
+ : 'Column "' . esc_html($column_name) . '" not found';
201
+ }
202
+ }
203
+
204
+ public function reorder_items_str($a, $b){
205
+ $result = strcmp($a->{$this->_orderby}, $b->{$this->_orderby});
206
+ return ($this->_order === 'asc') ? $result : -$result;
207
+ }
208
+
209
+ public function reorder_items_int($a, $b){
210
+ $result = $a->{$this->_orderby} - $b->{$this->_orderby};
211
+ return ($this->_order === 'asc') ? $result : -$result;
212
+ }
213
+
214
+ public function meta_formatter($name, $value){
215
+ switch(true){
216
+
217
+ case $name == '%Message%':
218
+ return esc_html($value);
219
+
220
+ case in_array($name, array('%MetaValue%', '%MetaValueOld%', '%MetaValueNew%')):
221
+ return '<strong>' . (
222
+ strlen($value) > 50 ? (esc_html(substr($value, 0, 50)) . '&hellip;') : esc_html($value)
223
+ ) . '</strong>';
224
+
225
+ case strncmp($value, 'http://', 7) === 0:
226
+ case strncmp($value, 'https://', 7) === 0:
227
+ return '<a href="' . esc_html($value) . '"'
228
+ . ' title="' . esc_html($value) . '"'
229
+ . ' target="_blank">'
230
+ . esc_html(parse_url($value, PHP_URL_HOST)) . '/&hellip;/'
231
+ . esc_html(basename(parse_url($value, PHP_URL_PATH)))
232
+ . '</a>';
233
+
234
+ default:
235
+ return '<strong>' . esc_html($value) . '</strong>';
236
+ }
237
+ }
238
+
239
+ protected function is_multisite(){
240
+ return $this->_plugin->IsMultisite();
241
+ }
242
+
243
+ protected function is_main_blog(){
244
+ return get_current_blog_id() == 1;
245
+ }
246
+
247
+ protected function is_specific_view(){
248
+ return isset($_REQUEST['wsal-cbid']) && $_REQUEST['wsal-cbid'] != '0';
249
+ }
250
+
251
+ protected function get_specific_view(){
252
+ return isset($_REQUEST['wsal-cbid']) ? (int)$_REQUEST['wsal-cbid'] : 0;
253
+ }
254
+
255
+ protected function get_view_site_id(){
256
+ switch(true){
257
+
258
+ // non-multisite
259
+ case !$this->is_multisite():
260
+ return 0;
261
+
262
+ // multisite + main site view
263
+ case $this->is_main_blog() && !$this->is_specific_view():
264
+ return 0;
265
+
266
+ // multisite + switched site view
267
+ case $this->is_main_blog() && $this->is_specific_view():
268
+ return $this->get_specific_view();
269
+
270
+ // multisite + local site view
271
+ default:
272
+ return get_current_blog_id();
273
+
274
+ }
275
+ }
276
+
277
+ public function prepare_items() {
278
+ $per_page = $this->_plugin->settings->GetViewPerPage();
279
+
280
+ $columns = $this->get_columns();
281
+ $hidden = array();
282
+ $sortable = $this->get_sortable_columns();
283
+
284
+ $this->_column_headers = array($columns, $hidden, $sortable);
285
+
286
+ //$this->process_bulk_action();
287
+
288
+ $query = new WSAL_DB_OccurrenceQuery('WSAL_DB_Occurrence');
289
+ $bid = (int)$this->get_view_site_id();
290
+ if ($bid) $query->where[] = 'site_id = '.$bid;
291
+ $query->order[] = 'created_on DESC';
292
+
293
+ $query = apply_filters('wsal_auditlog_query', $query);
294
+
295
+ $total_items = $query->Count();
296
+
297
+ /** @deprecated */
298
+ //$data = $query->Execute();
299
+
300
+ if($total_items){
301
+ $this->_orderby = (!empty($_REQUEST['orderby']) && isset($sortable[$_REQUEST['orderby']])) ? $_REQUEST['orderby'] : 'created_on';
302
+ $this->_order = (!empty($_REQUEST['order']) && $_REQUEST['order']=='asc') ? 'ASC' : 'DESC';
303
+ $tmp = new WSAL_DB_Occurrence();
304
+ if(isset($tmp->{$this->_orderby})){
305
+ // TODO we used to use a custom comparator ... is it safe to let MySQL do the ordering now?
306
+ $query->order[] = $this->_orderby . ' ' . $this->_order;
307
+ /** @deprecated */
308
+ //$numorder = in_array($this->_orderby, array('code', 'type', 'created_on'));
309
+ //usort($data, array($this, $numorder ? 'reorder_items_int' : 'reorder_items_str'));
310
+ }
311
+ }
312
+
313
+ /** @todo Modify $query instead */
314
+ /** @deprecated */
315
+ //$data = array_slice($data, ($this->get_pagenum() - 1) * $per_page, $per_page);
316
+ $query->offset = ($this->get_pagenum() - 1) * $per_page;
317
+ $query->length = $per_page;
318
+
319
+ $this->items = $query->Execute();
320
+
321
+ $this->set_pagination_args( array(
322
+ 'total_items' => $total_items,
323
+ 'per_page' => $per_page,
324
+ 'total_pages' => ceil($total_items / $per_page)
325
+ ) );
326
+ }
327
+ }
classes/Autoloader.php CHANGED
@@ -32,7 +32,9 @@ class WSAL_Autoloader {
32
  if(strstr($class, $prefix) !== false){
33
  $file = $path . str_replace('_', DIRECTORY_SEPARATOR, substr($class, strlen($prefix))) . '.php';
34
  if(file_exists($file)){
 
35
  require_once($file);
 
36
  return class_exists($class, false) || interface_exists($class, false);
37
  }
38
  }
32
  if(strstr($class, $prefix) !== false){
33
  $file = $path . str_replace('_', DIRECTORY_SEPARATOR, substr($class, strlen($prefix))) . '.php';
34
  if(file_exists($file)){
35
+ $s = $this->plugin->profiler->Start('Autoload ' . basename($file));
36
  require_once($file);
37
+ $s->Stop();
38
  return class_exists($class, false) || interface_exists($class, false);
39
  }
40
  }
classes/DB/ActiveRecord.php CHANGED
@@ -272,6 +272,17 @@ abstract class WSAL_DB_ActiveRecord {
272
  return $result;
273
  }
274
 
 
 
 
 
 
 
 
 
 
 
 
275
  /**
276
  * Load multiple records from DB.
277
  * @param string $cond (Optional) Load condition (eg: 'some_id = %d' ).
@@ -315,12 +326,25 @@ abstract class WSAL_DB_ActiveRecord {
315
  * If no parameters are given, this counts the number of records in the DB table.
316
  * @param string $cond (Optional) Query condition.
317
  * @param array $args (Optional) Condition arguments.
 
318
  */
319
  public static function Count($cond = '%d', $args = array(1)){
320
  global $wpdb;
321
  $class = get_called_class();
322
  $temp = new $class();
323
- $sql = $wpdb->prepare('SELECT COUNT(*) FROM ' . $temp->GetTable() . ' WHERE '.$cond, $args);
 
 
 
 
 
 
 
 
 
 
 
 
324
  return (int)$wpdb->get_var($sql);
325
  }
326
 
272
  return $result;
273
  }
274
 
275
+ /**
276
+ * Delete records in DB matching a query.
277
+ * @param string $query Full SQL query.
278
+ * @param array $args (Optional) Query arguments.
279
+ */
280
+ public static function DeleteQuery($query, $args = array()){
281
+ global $wpdb;
282
+ $sql = count($args) ? $wpdb->prepare($query, $args) : $query;
283
+ $wpdb->query($sql);
284
+ }
285
+
286
  /**
287
  * Load multiple records from DB.
288
  * @param string $cond (Optional) Load condition (eg: 'some_id = %d' ).
326
  * If no parameters are given, this counts the number of records in the DB table.
327
  * @param string $cond (Optional) Query condition.
328
  * @param array $args (Optional) Condition arguments.
329
+ * @return int Number of matching records.
330
  */
331
  public static function Count($cond = '%d', $args = array(1)){
332
  global $wpdb;
333
  $class = get_called_class();
334
  $temp = new $class();
335
+ $sql = $wpdb->prepare('SELECT COUNT(*) FROM ' . $temp->GetTable() . ' WHERE ' . $cond, $args);
336
+ return (int)$wpdb->get_var($sql);
337
+ }
338
+
339
+ /**
340
+ * Count records in the DB matching a query.
341
+ * @param string $query Full SQL query.
342
+ * @param array $args (Optional) Query arguments.
343
+ * @return int Number of matching records.
344
+ */
345
+ public static function CountQuery($query, $args = array()){
346
+ global $wpdb;
347
+ $sql = count($args) ? $wpdb->prepare($query, $args) : $query;
348
  return (int)$wpdb->get_var($sql);
349
  }
350
 
classes/DB/OccurrenceQuery.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_DB_OccurrenceQuery extends WSAL_DB_Query {
4
+ const LIKE_LEFT = 'l';
5
+ const LIKE_RIGHT = 'r';
6
+
7
+ /**
8
+ * Contains meta-specific arguments to be AND'ed together
9
+ * @var array
10
+ */
11
+ public $meta_where = array();
12
+
13
+ /**
14
+ * Contains arguments to be used in meta conditions.
15
+ * @var array
16
+ */
17
+ public $meta_args = array();
18
+
19
+ public function GetCond(){
20
+ $cond = parent::GetCond();
21
+ if (count($this->meta_where)) {
22
+ $tmp = new WSAL_DB_Meta();
23
+ $cond[] = 'id IN (
24
+ SELECT DISTINCT occurrence_id
25
+ FROM ' . $tmp->GetTable() . '
26
+ WHERE ' . implode(' AND ', $this->meta_where) . '
27
+ )';
28
+ }
29
+ return $cond;
30
+ }
31
+
32
+ public function GetArgs(){
33
+ $args = parent::GetArgs();
34
+ foreach ($this->meta_args as $arg) $args[] = $arg;
35
+ return $args;
36
+ }
37
+
38
+ /**
39
+ * Find occurrences matching an exact named meta value.
40
+ * @param string $name Meta name.
41
+ * @param string $value Meta value.
42
+ */
43
+ public function WhereMetaIs($name, $value){
44
+ $this->meta_where[] = 'name = %s AND value = %s';
45
+ $this->meta_args[] = $name;
46
+ $this->meta_args[] = $value;
47
+ }
48
+
49
+ /**
50
+ * Find occurrences matching a named meta containing a value.
51
+ * @param string $name Meta name.
52
+ * @param string $value Meta value.
53
+ * @param string $type Where to check for (left, right, both or none) see LIKE_* constants
54
+ */
55
+ public function WhereMetaLike($name, $value, $type){
56
+ $this->meta_where[] = 'name = %s AND value LIKE %s';
57
+ $this->meta_args[] = $name;
58
+ $value = esc_sql($value);
59
+ if (strpos($type, self::LIKE_LEFT) !== false) $value = '%' . $value;
60
+ if (strpos($type, self::LIKE_RIGHT) !== false) $value = $value . '%';
61
+ $this->meta_args[] = $value;
62
+ }
63
+
64
+ /**
65
+ * Find occurrences matching a meta condition.
66
+ * @param string $cond Meta condition.
67
+ * @param array $args Condition arguments.
68
+ */
69
+ public function WhereMeta($cond, $args){
70
+ $this->meta_where[] = $cond;
71
+ foreach ($args as $arg) $this->meta_args[] = $arg;
72
+ }
73
+
74
+ public function Delete(){
75
+ // delete meta data: back up columns, remove them for DELETE and generate sql
76
+ $cols = $this->columns;
77
+ $this->columns = array('occurrence_id');
78
+ $tmp = new WSAL_DB_Meta();
79
+ $sql = 'DELETE FROM ' . $tmp->GetTable() . ' WHERE occurrence_id IN (' . $this->GetSql('select') . ')';
80
+
81
+ // restore columns
82
+ $this->columns = $cols;
83
+
84
+ // execute query
85
+ call_user_func(array($this->ar_cls, 'DeleteQuery'), $sql, $this->GetArgs());
86
+
87
+ // delete occurrences
88
+ parent::Delete();
89
+ }
90
+ }
classes/DB/Query.php CHANGED
@@ -2,7 +2,6 @@
2
 
3
  /**
4
  * @todo Add group-by support
5
- * @todo Add limit/top support
6
  */
7
  class WSAL_DB_Query {
8
  /**
@@ -51,6 +50,18 @@ class WSAL_DB_Query {
51
  */
52
  public $args = array();
53
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  /**
55
  * @param string $ar_class Name of class that extends ActiveRecord class.
56
  */
@@ -68,20 +79,31 @@ class WSAL_DB_Query {
68
  /**
69
  * @return string Generated sql.
70
  */
71
- public function GetSql(){
 
72
  switch($this->GetDbType()){
73
  case 'mysql':
74
- return 'SELECT ' . implode(',', $this->columns)
75
  . ' FROM ' . implode(',', $this->from)
76
  . (count($this->joins) ? implode(' ', $this->where) : '')
77
- . (count($this->where) ? (' WHERE ' . implode(' AND ', $this->where)) : '')
 
 
78
  . (count($this->order) ? (' ORDER BY ' . implode(', ', $this->order)) : '')
 
79
  ;
80
  default:
81
  throw new Exception('SQL generation for "' . $this->GetDbType() . '" databases is not supported.');
82
  }
83
  }
84
 
 
 
 
 
 
 
 
85
  /**
86
  * @return array Arguments used in query.
87
  */
@@ -95,4 +117,46 @@ class WSAL_DB_Query {
95
  public function Execute(){
96
  return call_user_func(array($this->ar_cls, 'LoadMultiQuery'), $this->GetSql(), $this->GetArgs());
97
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
2
 
3
  /**
4
  * @todo Add group-by support
 
5
  */
6
  class WSAL_DB_Query {
7
  /**
50
  */
51
  public $args = array();
52
 
53
+ /**
54
+ * The amount of records to skip in result.
55
+ * @var int
56
+ */
57
+ public $offset = 0;
58
+
59
+ /**
60
+ * The maximum number of records in result.
61
+ * @var int
62
+ */
63
+ public $length = 0;
64
+
65
  /**
66
  * @param string $ar_class Name of class that extends ActiveRecord class.
67
  */
79
  /**
80
  * @return string Generated sql.
81
  */
82
+ public function GetSql($verb = 'select'){
83
+ $where = $this->GetCond();
84
  switch($this->GetDbType()){
85
  case 'mysql':
86
+ return strtoupper($verb) . ' ' . implode(',', $this->columns)
87
  . ' FROM ' . implode(',', $this->from)
88
  . (count($this->joins) ? implode(' ', $this->where) : '')
89
+ . (count($where) ? (' WHERE ' . implode(' AND ', $where)) : '')
90
+ // @todo GROUP BY goes here
91
+ // @todo HAVING goes here
92
  . (count($this->order) ? (' ORDER BY ' . implode(', ', $this->order)) : '')
93
+ . ($this->length ? (' LIMIT ' . ($this->offset ? ($this->offset . ', ') : '') . ' ' . $this->length) : '')
94
  ;
95
  default:
96
  throw new Exception('SQL generation for "' . $this->GetDbType() . '" databases is not supported.');
97
  }
98
  }
99
 
100
+ /**
101
+ * @return array Array of conditions.
102
+ */
103
+ public function GetCond(){
104
+ return $this->where;
105
+ }
106
+
107
  /**
108
  * @return array Arguments used in query.
109
  */
117
  public function Execute(){
118
  return call_user_func(array($this->ar_cls, 'LoadMultiQuery'), $this->GetSql(), $this->GetArgs());
119
  }
120
+
121
+ /**
122
+ * @return int Use query for counting records.
123
+ */
124
+ public function Count(){
125
+ // back up columns, use COUNT as default column and generate sql
126
+ $cols = $this->columns;
127
+ $this->columns = array('COUNT(*)');
128
+ $sql = $this->GetSql();
129
+
130
+ // restore columns
131
+ $this->columns = $cols;
132
+
133
+ // execute query and return result
134
+ return call_user_func(array($this->ar_cls, 'CountQuery'), $sql, $this->GetArgs());
135
+ }
136
+
137
+ /**
138
+ * Find occurrences matching a condition.
139
+ * @param string $cond The condition.
140
+ * @param array $args Condition arguments.
141
+ */
142
+ public function Where($cond, $args){
143
+ $this->where[] = $cond;
144
+ foreach ($args as $arg) $this->args[] = $arg;
145
+ }
146
+
147
+ /**
148
+ * Use query for deleting records.
149
+ */
150
+ public function Delete(){
151
+ // back up columns, remove them for DELETE and generate sql
152
+ $cols = $this->columns;
153
+ $this->columns = array();
154
+ $sql = $this->GetSql('delete');
155
+
156
+ // restore columns
157
+ $this->columns = $cols;
158
+
159
+ // execute query
160
+ call_user_func(array($this->ar_cls, 'DeleteQuery'), $sql, $this->GetArgs());
161
+ }
162
  }
classes/LicenseManager.php CHANGED
@@ -49,9 +49,9 @@ class WSAL_LicenseManager {
49
 
50
  $api_params = array(
51
  'edd_action'=> 'activate_license',
52
- 'license' => $license,
53
- 'item_name' => urlencode($name),
54
- 'url' => home_url()
55
  );
56
 
57
  $response = wp_remote_get(
@@ -60,7 +60,7 @@ class WSAL_LicenseManager {
60
  );
61
 
62
  if (is_wp_error($response)) {
63
- $this->plugin->settings->SetLicenseErrors($name, 'Invalid Licensing Server Response');
64
  return false;
65
  }
66
 
@@ -68,8 +68,11 @@ class WSAL_LicenseManager {
68
 
69
  if(is_object($license_data)){
70
  $this->plugin->settings->SetLicenseStatus($name, $license_data->license);
71
- if($license_data->license !== 'valid')
72
- $this->plugin->settings->SetLicenseErrors($name, 'License Not Valid');
 
 
 
73
  }else{
74
  $this->plugin->settings->SetLicenseErrors($name, 'Unexpected Licensing Server Response');
75
  }
49
 
50
  $api_params = array(
51
  'edd_action'=> 'activate_license',
52
+ 'license' => urlencode($license),
53
+ 'item_name' => urlencode($this->plugins[$name]['PluginData']['Name']),
54
+ 'url' => urlencode(home_url())
55
  );
56
 
57
  $response = wp_remote_get(
60
  );
61
 
62
  if (is_wp_error($response)) {
63
+ $this->plugin->settings->SetLicenseErrors($name, 'Invalid Licensing Server Response: ' . $response->get_error_message());
64
  return false;
65
  }
66
 
68
 
69
  if(is_object($license_data)){
70
  $this->plugin->settings->SetLicenseStatus($name, $license_data->license);
71
+ if($license_data->license !== 'valid'){
72
+ $error = 'License Not Valid';
73
+ if (isset($license_data->error)) $error .= ': ' . ucfirst(str_replace('_', ' ', $license_data->error));
74
+ $this->plugin->settings->SetLicenseErrors($name, $error);
75
+ }
76
  }else{
77
  $this->plugin->settings->SetLicenseErrors($name, 'Unexpected Licensing Server Response');
78
  }
classes/Loggers/Database.php CHANGED
@@ -32,30 +32,20 @@ class WSAL_Loggers_Database extends WSAL_AbstractLogger {
32
 
33
  $is_date_e = $this->plugin->settings->IsPruningDateEnabled();
34
  $is_limt_e = $this->plugin->settings->IsPruningLimitEnabled();
 
35
 
36
- switch(true){
37
- case $is_date_e && $is_limt_e:
38
- $cond = 'created_on < %d ORDER BY created_on ASC LIMIT %d';
39
- $args = array($max_stamp, $max_items);
40
- break;
41
- case $is_date_e && !$is_limt_e:
42
- $cond = 'created_on < %d';
43
- $args = array($max_stamp);
44
- break;
45
- case !$is_date_e && $is_limt_e:
46
- $cond = '1 ORDER BY created_on ASC LIMIT %d';
47
- $args = array($max_items);
48
- break;
49
- case !$is_date_e && !$is_limt_e:
50
- return;
51
- }
52
- if(!isset($cond))return;
53
 
54
- $items = WSAL_DB_Occurrence::LoadMulti($cond, $args);
55
- if(!count($items))return;
56
 
57
- foreach($items as $item)$item->Delete();
58
- do_action('wsal_prune', $items, vsprintf($cond, $args));
 
 
 
 
59
  }
60
 
61
  }
32
 
33
  $is_date_e = $this->plugin->settings->IsPruningDateEnabled();
34
  $is_limt_e = $this->plugin->settings->IsPruningLimitEnabled();
35
+ if (!$is_date_e && !$is_limt_e) return; // pruning disabled
36
 
37
+ $query = new WSAL_DB_OccurrenceQuery('WSAL_DB_Occurrence');
38
+ $query->order[] = 'created_on ASC';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ if ($is_date_e) $query->Where('created_on < %d', array($max_stamp));
41
+ if ($is_limt_e) $query->length = (int)$max_items;
42
 
43
+ $count = $query->Count();
44
+ if (!$count) return; // nothing to delete
45
+
46
+ // delete data and notify system
47
+ $query->Delete();
48
+ do_action('wsal_prune', $count, vsprintf($query->GetSql(), $query->GetArgs()));
49
  }
50
 
51
  }
classes/Sensors/LogInOut.php CHANGED
@@ -18,14 +18,14 @@ class WSAL_Sensors_LogInOut extends WSAL_AbstractSensor {
18
  public function EventLogin($user_login, $user){
19
  $this->plugin->alerts->Trigger(1000, array(
20
  'Username' => $user_login,
21
- 'CurrentUserRoles' => $user->roles,
22
  ), true);
23
  }
24
 
25
  public function EventLogout(){
26
  $this->plugin->alerts->Trigger(1001, array(
27
  'CurrentUserID' => $this->_current_user->ID,
28
- 'CurrentUserRoles' => $this->_current_user->roles,
29
  ), true);
30
  }
31
 
18
  public function EventLogin($user_login, $user){
19
  $this->plugin->alerts->Trigger(1000, array(
20
  'Username' => $user_login,
21
+ 'CurrentUserRoles' => $this->plugin->settings->GetCurrentUserRoles($user->roles),
22
  ), true);
23
  }
24
 
25
  public function EventLogout(){
26
  $this->plugin->alerts->Trigger(1001, array(
27
  'CurrentUserID' => $this->_current_user->ID,
28
+ 'CurrentUserRoles' => $this->plugin->settings->GetCurrentUserRoles($this->_current_user->roles),
29
  ), true);
30
  }
31
 
classes/Sensors/System.php CHANGED
@@ -8,11 +8,12 @@ class WSAL_Sensors_System extends WSAL_AbstractSensor {
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
  }
8
  }
9
 
10
  /**
11
+ * @param int $count The number of deleted events.
12
+ * @param string $query Query that selected events for deletion.
13
  */
14
+ public function EventPruneEvents($count, $query){
15
  $this->plugin->alerts->Trigger(6000, array(
16
+ 'EventCount' => $count,
17
  'PruneQuery' => $query,
18
  ));
19
  }
classes/Settings.php CHANGED
@@ -217,6 +217,14 @@ class WSAL_Settings {
217
  return $this->_plugin->GetGlobalOption('pruning-limit-e', true);
218
  }
219
 
 
 
 
 
 
 
 
 
220
  protected $_disabled = null;
221
 
222
  public function GetDefaultDisabledAlerts(){
@@ -325,42 +333,60 @@ class WSAL_Settings {
325
  }
326
 
327
  /**
328
- * @param integer|WP_user $user User object to check.
329
- * @param string $action Type of action, either 'view' or 'edit'.
330
- * @return boolean If user has access or not.
331
  */
332
- public function UserCan($user, $action){
333
- if(is_int($user))$user = get_userdata($user);
334
  $allowed = array();
335
 
336
  switch($action){
337
  case 'view':
338
  $allowed = $this->GetAllowedPluginViewers();
339
  $allowed = array_merge($allowed, $this->GetAllowedPluginEditors());
340
- $allowed = array_merge($allowed, $this->GetSuperAdmins());
341
- $allowed = array_merge($allowed, $this->GetAdmins());
 
 
342
  break;
343
  case 'edit':
344
  $allowed = $this->GetAllowedPluginEditors();
345
- $allowed = array_merge($allowed, $this->_plugin->IsMultisite() ?
346
- $this->GetSuperAdmins() : $this->GetAdmins()
347
- );
 
 
348
  break;
349
  default:
350
  throw new Exception('Unknown action "'.$action.'".');
351
  }
352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  $check = array_merge(
354
  $user->roles,
355
  array($user->user_login)
356
  );
357
 
358
- if(is_multisite()){
359
- $allowed = array_merge($allowed, get_super_admins());
360
- }else{
361
- $allowed[] = 'administrator';
362
- }
363
-
364
  foreach($check as $item){
365
  if(in_array($item, $allowed)){
366
  return true;
@@ -370,6 +396,12 @@ class WSAL_Settings {
370
  return false;
371
  }
372
 
 
 
 
 
 
 
373
  public function IsIncognito(){
374
  return $this->_plugin->GetGlobalOption('hide-plugin');
375
  }
217
  return $this->_plugin->GetGlobalOption('pruning-limit-e', true);
218
  }
219
 
220
+ public function IsRestrictAdmins(){
221
+ return $this->_plugin->GetGlobalOption('restrict-admins', false);
222
+ }
223
+
224
+ public function SetRestrictAdmins($enable){
225
+ $this->_plugin->SetGlobalOption('restrict-admins', (bool)$enable);
226
+ }
227
+
228
  protected $_disabled = null;
229
 
230
  public function GetDefaultDisabledAlerts(){
333
  }
334
 
335
  /**
336
+ * Returns access tokens for a particular action.
337
+ * @param string $action Type of action.
338
+ * @return string[] List of tokens (usernames, roles etc).
339
  */
340
+ public function GetAccessTokens($action){
 
341
  $allowed = array();
342
 
343
  switch($action){
344
  case 'view':
345
  $allowed = $this->GetAllowedPluginViewers();
346
  $allowed = array_merge($allowed, $this->GetAllowedPluginEditors());
347
+ if (!$this->IsRestrictAdmins()) {
348
+ $allowed = array_merge($allowed, $this->GetSuperAdmins());
349
+ $allowed = array_merge($allowed, $this->GetAdmins());
350
+ }
351
  break;
352
  case 'edit':
353
  $allowed = $this->GetAllowedPluginEditors();
354
+ if (!$this->IsRestrictAdmins()) {
355
+ $allowed = array_merge($allowed, $this->_plugin->IsMultisite() ?
356
+ $this->GetSuperAdmins() : $this->GetAdmins()
357
+ );
358
+ }
359
  break;
360
  default:
361
  throw new Exception('Unknown action "'.$action.'".');
362
  }
363
 
364
+ if (!$this->IsRestrictAdmins()) {
365
+ if(is_multisite()){
366
+ $allowed = array_merge($allowed, get_super_admins());
367
+ }else{
368
+ $allowed[] = 'administrator';
369
+ }
370
+ }
371
+
372
+ return array_unique($allowed);
373
+ }
374
+
375
+ /**
376
+ * @param integer|WP_user $user User object to check.
377
+ * @param string $action Type of action, either 'view' or 'edit'.
378
+ * @return boolean If user has access or not.
379
+ */
380
+ public function UserCan($user, $action){
381
+ if(is_int($user))$user = get_userdata($user);
382
+
383
+ $allowed = $this->GetAccessTokens($action);
384
+
385
  $check = array_merge(
386
  $user->roles,
387
  array($user->user_login)
388
  );
389
 
 
 
 
 
 
 
390
  foreach($check as $item){
391
  if(in_array($item, $allowed)){
392
  return true;
396
  return false;
397
  }
398
 
399
+ public function GetCurrentUserRoles($baseRoles = null){
400
+ if ($baseRoles == null) $baseRoles = wp_get_current_user()->roles;
401
+ if (function_exists('is_super_admin') && is_super_admin()) $baseRoles[] = 'superadmin';
402
+ return $baseRoles;
403
+ }
404
+
405
  public function IsIncognito(){
406
  return $this->_plugin->GetGlobalOption('hide-plugin');
407
  }
classes/SimpleProfiler.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WSAL_SimpleProfiler {
4
+ protected $_items = array();
5
+
6
+ public function Start($name) {
7
+ $item = new WSAL_SimpleProfiler_Item($name);
8
+ $this->_items[] = $item;
9
+ return $item;
10
+ }
11
+
12
+ public function AsComment(){
13
+ echo '<!-- ' . PHP_EOL;
14
+ foreach($this->_items as $item){
15
+ echo ' ' . $item . PHP_EOL;
16
+ }
17
+ echo '-->' . PHP_EOL;
18
+ }
19
+
20
+ public function GetItems(){
21
+ return $this->_items;
22
+ }
23
+ }
24
+
25
+ class WSAL_SimpleProfiler_Item{
26
+ public function __construct($name){
27
+ $this->name = $name;
28
+ $this->t_bgn = microtime(true);
29
+ $this->m_bgn = memory_get_usage();
30
+ }
31
+
32
+ public function Stop(){
33
+ $this->t_end = microtime(true);
34
+ $this->m_end = memory_get_usage();
35
+ }
36
+
37
+ public function __toString(){
38
+ $t_diff = $this->t_end - $this->t_bgn;
39
+ $m_diff = $this->m_end - $this->m_bgn;
40
+ return number_format($t_diff, 6) . 's '
41
+ . str_pad(number_format($m_diff, 0), 12, ' ', STR_PAD_LEFT) . 'b '
42
+ . $this->name;
43
+ }
44
+ }
classes/ViewManager.php CHANGED
@@ -97,7 +97,8 @@ class WSAL_ViewManager {
97
  'read', // no capability requirement
98
  $this->views[0]->GetSafeViewName(),
99
  array($this, 'RenderViewBody'),
100
- $this->views[0]->GetIcon()
 
101
  );
102
 
103
  // add menu items
@@ -192,11 +193,11 @@ class WSAL_ViewManager {
192
  */
193
  public function RenderViewBody(){
194
  $view = $this->GetActiveView();
195
- ?><div class="wrap">
196
- <div id="icon-plugins" class="icon32"><br></div>
197
- <h2><?php echo esc_html($view->GetTitle()); ?></h2>
198
- <?php $view->Render(); ?>
199
- </div><?php
200
  }
201
 
202
  /**
97
  'read', // no capability requirement
98
  $this->views[0]->GetSafeViewName(),
99
  array($this, 'RenderViewBody'),
100
+ $this->views[0]->GetIcon(),
101
+ '2.5' // right after dashboard
102
  );
103
 
104
  // add menu items
193
  */
194
  public function RenderViewBody(){
195
  $view = $this->GetActiveView();
196
+ ?><div class="wrap"><?php
197
+ $view->RenderIcon();
198
+ $view->RenderTitle();
199
+ $view->RenderContent();
200
+ ?></div><?php
201
  }
202
 
203
  /**
classes/Views/AuditLog.php CHANGED
@@ -1,12 +1,13 @@
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'));
@@ -35,19 +36,26 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
35
  return 1;
36
  }
37
 
 
 
 
 
 
38
  public function Render(){
39
  if(!$this->_plugin->settings->CurrentUserCan('view')){
40
  wp_die( __( 'You do not have sufficient permissions to access this page.' , 'wp-security-audit-log') );
41
  }
42
 
43
- $this->_listview->prepare_items();
44
 
45
  ?><form id="audit-log-viewer" method="post">
46
- <input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
47
- <input type="hidden" id="wsal-cbid" name="wsal-cbid" value="<?php echo esc_attr(isset($_REQUEST['wsal-cbid']) ? $_REQUEST['wsal-cbid'] : ''); ?>" />
48
- <?php do_action('wsal_auditlog_before_view', $this->_listview); ?>
49
- <?php $this->_listview->display(); ?>
50
- <?php do_action('wsal_auditlog_after_view', $this->_listview); ?>
 
 
51
  </form><?php
52
 
53
  ?><script type="text/javascript">
@@ -100,6 +108,8 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
100
  $old = (int)$_REQUEST['logcount'];
101
  $max = 40; // 40*500msec = 20sec
102
 
 
 
103
  do{
104
  $new = WSAL_DB_Occurrence::Count();
105
  usleep(500000); // 500msec
@@ -129,7 +139,7 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
129
 
130
  $search = $_REQUEST['search'];
131
 
132
- foreach($this->_listview->get_sites() as $site){
133
  if(stripos($site->blogname, $search) !== false)
134
  $grp1[] = $site;
135
  else
@@ -160,325 +170,4 @@ class WSAL_Views_AuditLog extends WSAL_AbstractView {
160
  filemtime($this->_plugin->GetBaseDir() . '/js/auditlog.js')
161
  );
162
  }
163
-
164
- }
165
-
166
- require_once(ABSPATH . 'wp-admin/includes/admin.php');
167
- require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
168
-
169
- class WSAL_Views_AuditLogList_Internal extends WP_List_Table {
170
-
171
- /**
172
- * @var WpSecurityAuditLog
173
- */
174
- protected $_plugin;
175
-
176
- public function __construct($plugin){
177
- $this->_plugin = $plugin;
178
-
179
- $this->_gmt_offset_sec = get_option('gmt_offset') * HOUR_IN_SECONDS;
180
-
181
- parent::__construct(array(
182
- 'singular' => 'log',
183
- 'plural' => 'logs',
184
- 'ajax' => true,
185
- 'screen' => 'interval-list',
186
- ));
187
- }
188
-
189
- protected $_gmt_offset_sec = 0;
190
-
191
- public function no_items(){
192
- _e('No events so far.', 'wp-security-audit-log');
193
- }
194
-
195
- public function extra_tablenav($which){
196
- // items-per-page widget
197
- $o = __('Other', 'wp-security-audit-log');
198
- $p = $this->_plugin->settings->GetViewPerPage();
199
- $items = array($o, 5, 10, 15, 30, 50);
200
- if (!in_array($p, $items)) $items[] = $p;
201
- if ($p == $o || $p == 0) $p = $o[1]; // a sane default if things goes bust
202
-
203
- ?><div class="wsal-ipp wsal-ipp-<?php echo $which; ?>">
204
- <?php _e('Show ', 'wp-security-audit-log'); ?>
205
- <select class="wsal-ipps" onfocus="WsalIppsFocus(value);" onchange="WsalIppsChange(value);">
206
- <?php foreach($items as $item){ ?>
207
- <option
208
- value="<?php echo is_string($item) ? '' : $item; ?>"
209
- <?php if($item == $p)echo 'selected="selected"'; ?>><?php
210
- echo $item;
211
- ?></option>
212
- <?php } ?>
213
- </select>
214
- <?php _e(' Items', 'wp-security-audit-log'); ?>
215
- </div><?php
216
-
217
- // show site alerts widget
218
- if($this->is_multisite() && $this->is_main_blog()){
219
- $curr = $this->get_view_site_id();
220
- ?><div class="wsal-ssa wsal-ssa-<?php echo $which; ?>">
221
- <?php if($this->get_site_count() > 15){ ?>
222
- <?php $curr = $curr ? get_blog_details($curr) : null; ?>
223
- <?php $curr = $curr ? ($curr->blogname . ' (' . $curr->domain . ')') : 'All Sites'; ?>
224
- <input type="text" class="wsal-ssas" value="<?php echo esc_attr($curr); ?>"/>
225
- <?php }else{ ?>
226
- <select class="wsal-ssas" onchange="WsalSsasChange(value);">
227
- <option value="0"><?php _e('All Sites', 'wp-security-audit-log'); ?></option>
228
- <?php foreach($this->get_sites() as $info){ ?>
229
- <option value="<?php echo $info->blog_id; ?>"
230
- <?php if($info->blog_id == $curr)echo 'selected="selected"'; ?>><?php
231
- echo esc_html($info->blogname) . ' (' . esc_html($info->domain) . ')';
232
- ?></option>
233
- <?php } ?>
234
- </select>
235
- <?php } ?>
236
- </div><?php
237
- }
238
- }
239
-
240
- /**
241
- * @param int|null $limit Maximum number of sites to return (null = no limit).
242
- * @return object Object with keys: blog_id, blogname, domain
243
- */
244
- public function get_sites($limit = null){
245
- global $wpdb;
246
-
247
- // build query
248
- $sql = 'SELECT blog_id, domain FROM ' . $wpdb->blogs;
249
- if(!is_null($limit))$sql .= ' LIMIT ' . $limit;
250
-
251
- // execute query
252
- $res = $wpdb->get_results($sql);
253
-
254
- // modify result
255
- foreach($res as $row){
256
- $row->blogname = get_blog_option($row->blog_id, 'blogname');
257
- }
258
-
259
- // return result
260
- return $res;
261
- }
262
-
263
- /**
264
- * @return int The number of sites on the network.
265
- */
266
- public function get_site_count(){
267
- global $wpdb;
268
- $sql = 'SELECT COUNT(*) FROM ' . $wpdb->blogs;
269
- return (int)$wpdb->get_var($sql);
270
- }
271
-
272
- public function get_columns(){
273
- $cols = array(
274
- //'cb' => '<input type="checkbox" />',
275
- //'read' => __('Read', 'wp-security-audit-log'),
276
- 'type' => __('Code', 'wp-security-audit-log'),
277
- 'code' => __('Type', 'wp-security-audit-log'),
278
- 'crtd' => __('Date', 'wp-security-audit-log'),
279
- 'user' => __('Username', 'wp-security-audit-log'),
280
- 'scip' => __('Source IP', 'wp-security-audit-log'),
281
- );
282
- if($this->is_multisite() && $this->is_main_blog() && !$this->is_specific_view()){
283
- $cols['site'] = __('Site', 'wp-security-audit-log');
284
- }
285
- $cols['mesg'] = __('Message', 'wp-security-audit-log');
286
- if($this->_plugin->settings->IsDataInspectorEnabled()){
287
- $cols['data'] = '';
288
- }
289
- return $cols;
290
- }
291
-
292
- public function column_cb(WSAL_DB_Occurrence $item){
293
- return '<input type="checkbox" value="'.$item->id.'" '
294
- . 'name="'.esc_attr($this->_args['singular']).'[]"/>';
295
- }
296
-
297
- public function get_sortable_columns(){
298
- return array(
299
- 'read' => array('is_read', false),
300
- 'code' => array('code', false),
301
- 'type' => array('alert_id', false),
302
- 'crtd' => array('created_on', true),
303
- 'user' => array('user', false),
304
- 'scip' => array('scip', false),
305
- 'site' => array('site', false),
306
- );
307
- }
308
-
309
- public function column_default(WSAL_DB_Occurrence $item, $column_name){
310
- switch($column_name){
311
- case 'read':
312
- return '<span class="log-read log-read-'
313
- . ($item->is_read ? 'old' : 'new')
314
- . '" title="' . __('Click to toggle.', 'wp-security-audit-log') . '"></span>';
315
- case 'type':
316
- return str_pad($item->alert_id, 4, '0', STR_PAD_LEFT);
317
- case 'code':
318
- $code = $this->_plugin->alerts->GetAlert($item->alert_id);
319
- $code = $code ? $code->code : 0;
320
- $const = (object)array('name' => 'E_UNKNOWN', 'value' => 0, 'description' => __('Unknown error code.', 'wp-security-audit-log'));
321
- $const = $this->_plugin->constants->GetConstantBy('value', $code, $const);
322
- return '<span class="log-type log-type-' . $const->value
323
- . '" title="' . esc_html($const->name . ': ' . $const->description) . '"></span>';
324
- case 'crtd':
325
- return $item->created_on ? (
326
- str_replace(
327
- '$$$',
328
- substr(number_format(fmod($item->created_on + $this->_gmt_offset_sec, 1), 3), 2),
329
- date('Y-m-d<\b\r>h:i:s.$$$&\n\b\s\p;A', $item->created_on + $this->_gmt_offset_sec)
330
- )
331
- ) : '<i>unknown</i>';
332
- case 'user':
333
- $username = $item->GetUsername();
334
- if($username && ($user = get_user_by('login', $username))){
335
- $image = get_avatar($user->ID, 32);
336
- $uhtml = '<a href="' . admin_url('user-edit.php?user_id=' . $user->ID)
337
- . '" target="_blank">' . esc_html($user->display_name) . '</a>';
338
- $roles = $item->GetUserRoles();
339
- $roles = (is_array($roles) && count($roles))
340
- ? __(esc_html(ucwords(implode(', ', $roles))))
341
- : '<i>' . __('Unknown', 'wp-security-audit-log') . '</i>';
342
- }else{
343
- $image = get_avatar(0, 32);
344
- $uhtml = '<i>' . __('Unknown', 'wp-security-audit-log') . '</i>';
345
- $roles = '<i>' . __('System', 'wp-security-audit-log') . '</i>';
346
- }
347
- return $image . $uhtml . '<br/>' . $roles;
348
- case 'scip':
349
- return !is_null($item->GetSourceIP()) ? esc_html($item->GetSourceIP()) : '<i>unknown</i>';
350
- case 'site':
351
- $info = get_blog_details($item->site_id, true);
352
- return !$info ? ('Unknown Site '.$item->site_id)
353
- : ('<a href="' . esc_attr($info->siteurl) . '">' . esc_html($info->blogname) . '</a>');
354
- case 'mesg':
355
- return '<div id="Event' . $item->id . '">' . $item->GetMessage(array($this, 'meta_formatter')) . '</div>';
356
- case 'data':
357
- $url = admin_url('admin-ajax.php') . '?action=AjaxInspector&amp;occurrence=' . $item->id;
358
- return '<a class="more-info thickbox" title="' . __('Alert Data Inspector', 'wp-security-audit-log') . '"'
359
- . ' href="' . $url . '&amp;TB_iframe=true&amp;width=600&amp;height=550">&hellip;</a>';
360
- default:
361
- return isset($item->$column_name)
362
- ? esc_html($item->$column_name)
363
- : 'Column "' . esc_html($column_name) . '" not found';
364
- }
365
- }
366
-
367
- public function reorder_items_str($a, $b){
368
- $result = strcmp($a->{$this->_orderby}, $b->{$this->_orderby});
369
- return ($this->_order === 'asc') ? $result : -$result;
370
- }
371
-
372
- public function reorder_items_int($a, $b){
373
- $result = $a->{$this->_orderby} - $b->{$this->_orderby};
374
- return ($this->_order === 'asc') ? $result : -$result;
375
- }
376
-
377
- public function meta_formatter($name, $value){
378
- switch(true){
379
-
380
- case $name == '%Message%':
381
- return esc_html($value);
382
-
383
- case in_array($name, array('%MetaValue%', '%MetaValueOld%', '%MetaValueNew%')):
384
- return '<strong>' . (
385
- strlen($value) > 50 ? (esc_html(substr($value, 0, 50)) . '&hellip;') : esc_html($value)
386
- ) . '</strong>';
387
-
388
- case strncmp($value, 'http://', 7) === 0:
389
- case strncmp($value, 'https://', 7) === 0:
390
- return '<a href="' . esc_html($value) . '"'
391
- . ' title="' . esc_html($value) . '"'
392
- . ' target="_blank">'
393
- . esc_html(parse_url($value, PHP_URL_HOST)) . '/&hellip;/'
394
- . esc_html(basename(parse_url($value, PHP_URL_PATH)))
395
- . '</a>';
396
-
397
- default:
398
- return '<strong>' . esc_html($value) . '</strong>';
399
- }
400
- }
401
-
402
- protected function is_multisite(){
403
- return $this->_plugin->IsMultisite();
404
- }
405
-
406
- protected function is_main_blog(){
407
- return get_current_blog_id() == 1;
408
- }
409
-
410
- protected function is_specific_view(){
411
- return isset($_REQUEST['wsal-cbid']) && $_REQUEST['wsal-cbid'] != '0';
412
- }
413
-
414
- protected function get_specific_view(){
415
- return isset($_REQUEST['wsal-cbid']) ? (int)$_REQUEST['wsal-cbid'] : 0;
416
- }
417
-
418
- protected function get_view_site_id(){
419
- switch(true){
420
-
421
- // non-multisite
422
- case !$this->is_multisite():
423
- return 0;
424
-
425
- // multisite + main site view
426
- case $this->is_main_blog() && !$this->is_specific_view():
427
- return 0;
428
-
429
- // multisite + switched site view
430
- case $this->is_main_blog() && $this->is_specific_view():
431
- return $this->get_specific_view();
432
-
433
- // multisite + local site view
434
- default:
435
- return get_current_blog_id();
436
-
437
- }
438
- }
439
-
440
- public function prepare_items() {
441
- $per_page = $this->_plugin->settings->GetViewPerPage();
442
-
443
- $columns = $this->get_columns();
444
- $hidden = array();
445
- $sortable = $this->get_sortable_columns();
446
-
447
- $this->_column_headers = array($columns, $hidden, $sortable);
448
-
449
- //$this->process_bulk_action();
450
-
451
- $query = new WSAL_DB_Query('WSAL_DB_Occurrence');
452
- $bid = (int)$this->get_view_site_id();
453
- if ($bid) $query->where[] = 'site_id = '.$bid;
454
- $query->order[] = 'created_on DESC';
455
-
456
- $data = apply_filters('wsal_auditlog_query', $query);
457
-
458
- $data = $query->Execute();
459
-
460
- if(count($data)){
461
- $this->_orderby = (!empty($_REQUEST['orderby']) && isset($sortable[$_REQUEST['orderby']])) ? $_REQUEST['orderby'] : 'created_on';
462
- $this->_order = (!empty($_REQUEST['order']) && $_REQUEST['order']=='asc') ? 'asc' : 'desc';
463
- if(isset($data[0]->{$this->_orderby})){
464
- $numorder = in_array($this->_orderby, array('code', 'type', 'created_on'));
465
- usort($data, array($this, $numorder ? 'reorder_items_int' : 'reorder_items_str'));
466
- }
467
- }
468
-
469
- $current_page = $this->get_pagenum();
470
-
471
- $total_items = count($data);
472
-
473
- $data = array_slice($data, ($current_page - 1) * $per_page, $per_page);
474
-
475
- $this->items = $data;
476
-
477
- $this->set_pagination_args( array(
478
- 'total_items' => $total_items,
479
- 'per_page' => $per_page,
480
- 'total_pages' => ceil($total_items / $per_page)
481
- ) );
482
- }
483
-
484
  }
1
  <?php
2
 
3
  class WSAL_Views_AuditLog extends WSAL_AbstractView {
4
+ /**
5
+ * @var WSAL_AuditLogListView
6
+ */
7
  protected $_listview;
8
 
9
  public function __construct(WpSecurityAuditLog $plugin) {
10
  parent::__construct($plugin);
 
11
  add_action('wp_ajax_AjaxInspector', array($this, 'AjaxInspector'));
12
  add_action('wp_ajax_AjaxRefresh', array($this, 'AjaxRefresh'));
13
  add_action('wp_ajax_AjaxSetIpp', array($this, 'AjaxSetIpp'));
36
  return 1;
37
  }
38
 
39
+ protected function GetListView(){
40
+ if (is_null($this->_listview)) $this->_listview = new WSAL_AuditLogListView($this->_plugin);
41
+ return $this->_listview;
42
+ }
43
+
44
  public function Render(){
45
  if(!$this->_plugin->settings->CurrentUserCan('view')){
46
  wp_die( __( 'You do not have sufficient permissions to access this page.' , 'wp-security-audit-log') );
47
  }
48
 
49
+ $this->GetListView()->prepare_items();
50
 
51
  ?><form id="audit-log-viewer" method="post">
52
+ <div id="audit-log-viewer-content">
53
+ <input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
54
+ <input type="hidden" id="wsal-cbid" name="wsal-cbid" value="<?php echo esc_attr(isset($_REQUEST['wsal-cbid']) ? $_REQUEST['wsal-cbid'] : ''); ?>" />
55
+ <?php do_action('wsal_auditlog_before_view', $this->GetListView()); ?>
56
+ <?php $this->GetListView()->display(); ?>
57
+ <?php do_action('wsal_auditlog_after_view', $this->GetListView()); ?>
58
+ </div>
59
  </form><?php
60
 
61
  ?><script type="text/javascript">
108
  $old = (int)$_REQUEST['logcount'];
109
  $max = 40; // 40*500msec = 20sec
110
 
111
+ session_write_close(); // fixes session lock issue
112
+
113
  do{
114
  $new = WSAL_DB_Occurrence::Count();
115
  usleep(500000); // 500msec
139
 
140
  $search = $_REQUEST['search'];
141
 
142
+ foreach($this->GetListView()->get_sites() as $site){
143
  if(stripos($site->blogname, $search) !== false)
144
  $grp1[] = $site;
145
  else
170
  filemtime($this->_plugin->GetBaseDir() . '/js/auditlog.js')
171
  );
172
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
classes/Views/Licensing.php CHANGED
@@ -65,13 +65,13 @@ class WSAL_Views_Licensing extends WSAL_AbstractView {
65
  <input type="text" style="width: 360px; margin: 6px 0;"
66
  name="license[<?php echo esc_attr($name); ?>]"
67
  value="<?php echo esc_attr($licenseKey); ?>"/>
68
- </td><td>
69
  <?php if($licenseKey){ ?>
70
  <?php if($licenseStatus === 'valid'){ ?>
71
  <?php _e('Active', 'wp-security-audit-log'); ?>
72
  <?php }else{ ?>
73
  <?php _e('Inactive', 'wp-security-audit-log'); ?><br/>
74
- <small><?php echo esc_html($licenseErrors); ?></small>
75
  <?php } ?>
76
  <?php } ?>
77
  </td>
65
  <input type="text" style="width: 360px; margin: 6px 0;"
66
  name="license[<?php echo esc_attr($name); ?>]"
67
  value="<?php echo esc_attr($licenseKey); ?>"/>
68
+ </td><td style="vertical-align: middle;">
69
  <?php if($licenseKey){ ?>
70
  <?php if($licenseStatus === 'valid'){ ?>
71
  <?php _e('Active', 'wp-security-audit-log'); ?>
72
  <?php }else{ ?>
73
  <?php _e('Inactive', 'wp-security-audit-log'); ?><br/>
74
+ <small><?php echo esc_html($licenseErrors); ?></small>
75
  <?php } ?>
76
  <?php } ?>
77
  </td>
classes/Views/Sandbox.php CHANGED
@@ -77,6 +77,12 @@ class DummySiteCreatorTask extends WSAL_AbstractSandboxTask {
77
  }
78
  }
79
  new DummySiteCreatorTask();',
 
 
 
 
 
 
80
  );
81
 
82
  public function HandleError($code, $message, $filename = 'unknown', $lineno = 0){
@@ -192,8 +198,7 @@ new DummySiteCreatorTask();',
192
  </div>
193
  <label for="sandbox-snippet" style="float: left; line-height: 26px; display: inline-block; margin-right: 32px; border-right: 1px dotted #CCC; padding-right: 32px;">
194
  Use Snippet:
195
- <?php $code = json_encode(admin_url('admin.php?page=wsal-sandbox') . '&snippet='); ?>
196
- <select id="sandbox-snippet" onchange="location = <?php echo esc_attr($code); ?> + encodeURIComponent(this.value);"><?php
197
  foreach(array_keys($this->snippets) as $name){
198
  ?><option value="<?php echo esc_attr($name); ?>"<?php if($name == $snpt)echo ' selected="selected"'; ?>><?php echo $name; ?></option><?php
199
  }
@@ -262,6 +267,12 @@ new DummySiteCreatorTask();',
262
  //jQuery('#sandbox').submit();
263
  });
264
 
 
 
 
 
 
 
265
  function SandboxUpdateState(data){
266
  var ul = jQuery('<ul/>');
267
  for(var key in data)
77
  }
78
  }
79
  new DummySiteCreatorTask();',
80
+ 'Get Access Tokens' => '$settings = WpSecurityAuditLog::GetInstance()->settings;
81
+ return array(
82
+ \'view\' => $settings->GetAccessTokens(\'view\'),
83
+ \'edit\' => $settings->GetAccessTokens(\'edit\'),
84
+ );',
85
+ 'Show Profiler Results' => 'return array_map(\'strval\', WpSecurityAuditLog::GetInstance()->profiler->GetItems());'
86
  );
87
 
88
  public function HandleError($code, $message, $filename = 'unknown', $lineno = 0){
198
  </div>
199
  <label for="sandbox-snippet" style="float: left; line-height: 26px; display: inline-block; margin-right: 32px; border-right: 1px dotted #CCC; padding-right: 32px;">
200
  Use Snippet:
201
+ <select id="sandbox-snippet" onchange="SandboxUseSnippet(this.value);"><?php
 
202
  foreach(array_keys($this->snippets) as $name){
203
  ?><option value="<?php echo esc_attr($name); ?>"<?php if($name == $snpt)echo ' selected="selected"'; ?>><?php echo $name; ?></option><?php
204
  }
267
  //jQuery('#sandbox').submit();
268
  });
269
 
270
+ function SandboxUseSnippet(value){
271
+ jQuery('#sandbox-submit').attr('disabled', true);
272
+ location = <?php echo json_encode(admin_url('admin.php?page=wsal-sandbox')); ?>
273
+ + '&snippet=' + encodeURIComponent(value);
274
+ }
275
+
276
  function SandboxUpdateState(data){
277
  var ul = jQuery('<ul/>');
278
  for(var key in data)
classes/Views/Settings.php CHANGED
@@ -4,7 +4,10 @@ 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(){
@@ -47,6 +50,7 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
47
  $this->_plugin->settings->SetWidgetsEnabled($_REQUEST['EnableDashboardWidgets']);
48
  $this->_plugin->settings->SetAllowedPluginViewers(isset($_REQUEST['Viewers']) ? $_REQUEST['Viewers'] : array());
49
  $this->_plugin->settings->SetAllowedPluginEditors(isset($_REQUEST['Editors']) ? $_REQUEST['Editors'] : array());
 
50
  $this->_plugin->settings->SetRefreshAlertsEnabled($_REQUEST['EnableAuditViewRefresh']);
51
  $this->_plugin->settings->SetIncognito(isset($_REQUEST['Incognito']));
52
  $this->_plugin->settings->ClearDevOptions();
@@ -63,6 +67,14 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
63
  die($this->GetTokenType($_REQUEST['token']));
64
  }
65
 
 
 
 
 
 
 
 
 
66
  public function Render(){
67
  if(!$this->_plugin->settings->CurrentUserCan('edit')){
68
  wp_die( __( 'You do not have sufficient permissions to access this page.' , 'wp-security-audit-log') );
@@ -97,11 +109,6 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
97
  value="<?php echo esc_attr($this->_plugin->settings->GetPruningDate()); ?>"/>
98
  <span> <?php echo $text; ?></span>
99
  </fieldset>
100
- </td>
101
- </tr>
102
- <tr>
103
- <th></th>
104
- <td>
105
  <fieldset>
106
  <?php $text = __('(eg: 80)', 'wp-security-audit-log'); ?>
107
  <?php $nbld = $this->_plugin->settings->IsPruningLimitEnabled(); ?>
@@ -115,6 +122,15 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
115
  <?php echo __('alerts', 'wp-security-audit-log'); ?>
116
  <span><?php echo $text; ?></span>
117
  </fieldset>
 
 
 
 
 
 
 
 
 
118
  </td>
119
  </tr>
120
  <tr>
@@ -185,6 +201,21 @@ class WSAL_Views_Settings extends WSAL_AbstractView {
185
  </fieldset>
186
  </td>
187
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  <tr>
189
  <th><label for="aroption_on"><?php _e('Refresh Audit View', 'wp-security-audit-log'); ?></label></th>
190
  <td>
4
 
5
  public function __construct(WpSecurityAuditLog $plugin) {
6
  parent::__construct($plugin);
7
+ if (is_admin()) {
8
+ add_action('wp_ajax_AjaxCheckSecurityToken', array($this, 'AjaxCheckSecurityToken'));
9
+ add_action('wp_ajax_AjaxRunCleanup', array($this, 'AjaxRunCleanup'));
10
+ }
11
  }
12
 
13
  public function HasPluginShortcutLink(){
50
  $this->_plugin->settings->SetWidgetsEnabled($_REQUEST['EnableDashboardWidgets']);
51
  $this->_plugin->settings->SetAllowedPluginViewers(isset($_REQUEST['Viewers']) ? $_REQUEST['Viewers'] : array());
52
  $this->_plugin->settings->SetAllowedPluginEditors(isset($_REQUEST['Editors']) ? $_REQUEST['Editors'] : array());
53
+ $this->_plugin->settings->SetRestrictAdmins(isset($_REQUEST['RestrictAdmins']));
54
  $this->_plugin->settings->SetRefreshAlertsEnabled($_REQUEST['EnableAuditViewRefresh']);
55
  $this->_plugin->settings->SetIncognito(isset($_REQUEST['Incognito']));
56
  $this->_plugin->settings->ClearDevOptions();
67
  die($this->GetTokenType($_REQUEST['token']));
68
  }
69
 
70
+ public function AjaxRunCleanup(){
71
+ if(!$this->_plugin->settings->CurrentUserCan('view'))
72
+ die('Access Denied.');
73
+ $this->_plugin->CleanUp();
74
+ wp_redirect($this->GetUrl());
75
+ exit;
76
+ }
77
+
78
  public function Render(){
79
  if(!$this->_plugin->settings->CurrentUserCan('edit')){
80
  wp_die( __( 'You do not have sufficient permissions to access this page.' , 'wp-security-audit-log') );
109
  value="<?php echo esc_attr($this->_plugin->settings->GetPruningDate()); ?>"/>
110
  <span> <?php echo $text; ?></span>
111
  </fieldset>
 
 
 
 
 
112
  <fieldset>
113
  <?php $text = __('(eg: 80)', 'wp-security-audit-log'); ?>
114
  <?php $nbld = $this->_plugin->settings->IsPruningLimitEnabled(); ?>
122
  <?php echo __('alerts', 'wp-security-audit-log'); ?>
123
  <span><?php echo $text; ?></span>
124
  </fieldset>
125
+ <p class="description"><?php
126
+ echo __('Next Scheduled Cleanup is in ', 'wp-security-audit-log');
127
+ echo human_time_diff(current_time('timestamp'), $next = wp_next_scheduled('wsal_cleanup'));
128
+ echo '<!-- ' . date('dMy H:i:s', $next) . ' --> ';
129
+ echo sprintf(
130
+ __('(or %s)', 'wp-security-audit-log'),
131
+ '<a href="' . admin_url('admin-ajax.php?action=AjaxRunCleanup') . '">' . __('Run Manually', 'wp-security-audit-log') . '</a>'
132
+ );
133
+ ?></p>
134
  </td>
135
  </tr>
136
  <tr>
201
  </fieldset>
202
  </td>
203
  </tr>
204
+ <tr>
205
+ <th><label for="RestrictAdmins"><?php _e('Restrict Plugin Access', 'wp-security-audit-log'); ?></label></th>
206
+ <td>
207
+ <fieldset>
208
+ <input type="hidden" id="RestrictAdminsDefaultUser" value="<?php echo esc_attr(wp_get_current_user()->user_login); ?>"/>
209
+ <label for="RestrictAdmins">
210
+ <?php $ira = $this->_plugin->settings->IsRestrictAdmins(); ?>
211
+ <input type="checkbox" name="RestrictAdmins" id="RestrictAdmins"<?php if($ira)echo ' checked="checked"'; ?>/>
212
+ <span class="description">
213
+ <?php _e('By default all the administrators on this WordPress have access to manage this plugin.<br/>By enabling this option only the users specified in the two options above and your username will have access to view alerts and manage this plugin.', 'wp-security-audit-log'); ?>
214
+ </span>
215
+ </label>
216
+ </fieldset>
217
+ </td>
218
+ </tr>
219
  <tr>
220
  <th><label for="aroption_on"><?php _e('Refresh Audit View', 'wp-security-audit-log'); ?></label></th>
221
  <td>
classes/WidgetManager.php CHANGED
@@ -39,7 +39,7 @@ class WSAL_WidgetManager {
39
  </thead>
40
  <tbody><?php
41
  $url = 'admin.php?page=' . $this->_plugin->views->views[0]->GetSafeViewName();
42
- $fmt = array(new WSAL_Views_AuditLogList_Internal($this->_plugin), 'meta_formatter');
43
  foreach($results as $entry){
44
  ?><tr>
45
  <td><?php
39
  </thead>
40
  <tbody><?php
41
  $url = 'admin.php?page=' . $this->_plugin->views->views[0]->GetSafeViewName();
42
+ $fmt = array(new WSAL_AuditLogListView($this->_plugin), 'meta_formatter');
43
  foreach($results as $entry){
44
  ?><tr>
45
  <td><?php
defaults.php CHANGED
@@ -71,7 +71,7 @@ function wsaldefaults_wsal_init(WpSecurityAuditLog $wsal){
71
  array(2004, E_NOTICE, __('User created a new WordPress page and saved it as draft', 'wp-security-audit-log'), __('Created a new page called %PostTitle%. Page ID is %PostID%', 'wp-security-audit-log')),
72
  array(2005, E_NOTICE, __('User published a WorPress page', 'wp-security-audit-log'), __('Published a page called %PostTitle%. Page URL is %PostUrl%', 'wp-security-audit-log')),
73
  array(2006, E_NOTICE, __('User modified a published WordPress page', 'wp-security-audit-log'), __('Modified the published page %PostTitle%. Page URL is %PostUrl%', 'wp-security-audit-log')),
74
- array(2007, E_NOTICE, __('User modified a draft WordPress page', 'wp-security-audit-log'), __('Modified the draft page %PostTitle%. page ID is %PostID%', 'wp-security-audit-log')),
75
  array(2009, E_NOTICE, __('User permanently deleted a page from the trash', 'wp-security-audit-log'), __('Deleted the page %PostTitle%. Page ID is %PostID%', 'wp-security-audit-log')),
76
  array(2013, E_WARNING, __('User moved WordPress page to the trash', 'wp-security-audit-log'), __('Moved the page %PostTitle% to trash', 'wp-security-audit-log')),
77
  array(2015, E_CRITICAL, __('User restored a WordPress page from trash', 'wp-security-audit-log'), __('Restored page %PostTitle% from trash', 'wp-security-audit-log')),
71
  array(2004, E_NOTICE, __('User created a new WordPress page and saved it as draft', 'wp-security-audit-log'), __('Created a new page called %PostTitle%. Page ID is %PostID%', 'wp-security-audit-log')),
72
  array(2005, E_NOTICE, __('User published a WorPress page', 'wp-security-audit-log'), __('Published a page called %PostTitle%. Page URL is %PostUrl%', 'wp-security-audit-log')),
73
  array(2006, E_NOTICE, __('User modified a published WordPress page', 'wp-security-audit-log'), __('Modified the published page %PostTitle%. Page URL is %PostUrl%', 'wp-security-audit-log')),
74
+ array(2007, E_NOTICE, __('User modified a draft WordPress page', 'wp-security-audit-log'), __('Modified the draft page %PostTitle%. Page ID is %PostID%', 'wp-security-audit-log')),
75
  array(2009, E_NOTICE, __('User permanently deleted a page from the trash', 'wp-security-audit-log'), __('Deleted the page %PostTitle%. Page ID is %PostID%', 'wp-security-audit-log')),
76
  array(2013, E_WARNING, __('User moved WordPress page to the trash', 'wp-security-audit-log'), __('Moved the page %PostTitle% to trash', 'wp-security-audit-log')),
77
  array(2015, E_CRITICAL, __('User restored a WordPress page from trash', 'wp-security-audit-log'), __('Restored page %PostTitle% from trash', 'wp-security-audit-log')),
js/auditlog.js CHANGED
@@ -1,5 +1,7 @@
1
  var WsalData;
2
 
 
 
3
  function WsalAuditLogInit(_WsalData){
4
  WsalData = _WsalData;
5
  var WsalTkn = WsalData.autorefresh.token;
@@ -15,7 +17,10 @@ function WsalAuditLogInit(_WsalData){
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
  });
1
  var WsalData;
2
 
3
+ window['WsalAuditLogRefreshed'] = function(){};
4
+
5
  function WsalAuditLogInit(_WsalData){
6
  WsalData = _WsalData;
7
  var WsalTkn = WsalData.autorefresh.token;
17
  WsalAjx = null;
18
  if(data && data !== 'false'){
19
  WsalTkn = data;
20
+ jQuery('#audit-log-viewer').load(
21
+ location.href + ' #audit-log-viewer-content',
22
+ window['WsalAuditLogRefreshed']
23
+ );
24
  }
25
  WsalChk();
26
  });
js/settings.js CHANGED
@@ -34,4 +34,16 @@ jQuery(document).ready(function(){
34
  });
35
 
36
  jQuery('#ViewerList>span>a, #EditorList>span>a').click(RemoveSecToken);
 
 
 
 
 
 
 
 
 
 
 
 
37
  });
34
  });
35
 
36
  jQuery('#ViewerList>span>a, #EditorList>span>a').click(RemoveSecToken);
37
+
38
+ jQuery('#RestrictAdmins').change(function(){
39
+ var user = jQuery('#RestrictAdminsDefaultUser').val();
40
+ var fltr = function() { return this.value === user; };
41
+ if (this.checked && jQuery('#EditorList input').filter(fltr).length === 0) {
42
+ jQuery('#EditorList').append(
43
+ jQuery('<span class="sectoken-user"/>').text(user)
44
+ .prepend(jQuery('<input type="hidden" name="Editors[]"/>').val(user))
45
+ .append(jQuery('<a href="javascript:;" title="Remove">&times;</a>').click(RemoveSecToken))
46
+ );
47
+ }
48
+ });
49
  });
languages/wp-security-audit-log.pot CHANGED
@@ -2,9 +2,9 @@
2
  # This file is distributed under the same license as the WP Security Audit Log package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: WP Security Audit Log 1.2.6\n"
6
  "Report-Msgid-Bugs-To: http://wordpress.org/tag/wp-security-audit-log\n"
7
- "POT-Creation-Date: 2014-08-20 07:52:54+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
@@ -136,91 +136,27 @@ msgstr ""
136
  msgid "Professional WordPress security services provided by WP White Security"
137
  msgstr ""
138
 
139
- #: classes/Views/AuditLog.php:21 classes/Views/AuditLog.php:31
140
  msgid "Audit Log Viewer"
141
  msgstr ""
142
 
143
- #: classes/Views/AuditLog.php:40 classes/Views/Licensing.php:34
144
- #: classes/Views/Settings.php:68 classes/Views/ToggleAlerts.php:29
145
  msgid "You do not have sufficient permissions to access this page."
146
  msgstr ""
147
 
148
- #: classes/Views/AuditLog.php:58
149
  msgid "Please enter the number of alerts you would like to see on one page:"
150
  msgstr ""
151
 
152
- #: classes/Views/AuditLog.php:59 classes/Views/AuditLog.php:227
153
  msgid "All Sites"
154
  msgstr ""
155
 
156
- #: classes/Views/AuditLog.php:60
157
  msgid "No Results"
158
  msgstr ""
159
 
160
- #: classes/Views/AuditLog.php:192
161
- msgid "No events so far."
162
- msgstr ""
163
-
164
- #: classes/Views/AuditLog.php:197
165
- msgid "Other"
166
- msgstr ""
167
-
168
- #: classes/Views/AuditLog.php:204
169
- msgid "Show "
170
- msgstr ""
171
-
172
- #: classes/Views/AuditLog.php:214
173
- msgid " Items"
174
- msgstr ""
175
-
176
- #: classes/Views/AuditLog.php:276 classes/Views/ToggleAlerts.php:69
177
- msgid "Code"
178
- msgstr ""
179
-
180
- #: classes/Views/AuditLog.php:277 classes/Views/ToggleAlerts.php:70
181
- msgid "Type"
182
- msgstr ""
183
-
184
- #: classes/Views/AuditLog.php:278
185
- msgid "Date"
186
- msgstr ""
187
-
188
- #: classes/Views/AuditLog.php:279
189
- msgid "Username"
190
- msgstr ""
191
-
192
- #: classes/Views/AuditLog.php:280
193
- msgid "Source IP"
194
- msgstr ""
195
-
196
- #: classes/Views/AuditLog.php:283
197
- msgid "Site"
198
- msgstr ""
199
-
200
- #: classes/Views/AuditLog.php:285
201
- msgid "Message"
202
- msgstr ""
203
-
204
- #: classes/Views/AuditLog.php:314
205
- msgid "Click to toggle."
206
- msgstr ""
207
-
208
- #: classes/Views/AuditLog.php:320
209
- msgid "Unknown error code."
210
- msgstr ""
211
-
212
- #: classes/Views/AuditLog.php:341 classes/Views/AuditLog.php:344
213
- msgid "Unknown"
214
- msgstr ""
215
-
216
- #: classes/Views/AuditLog.php:345
217
- msgid "System"
218
- msgstr ""
219
-
220
- #: classes/Views/AuditLog.php:358
221
- msgid "Alert Data Inspector"
222
- msgstr ""
223
-
224
  #: classes/Views/Help.php:6 classes/Views/Help.php:14
225
  #: classes/Views/Help.php:25
226
  msgid "Help"
@@ -332,12 +268,12 @@ msgstr ""
332
  msgid "Licensing"
333
  msgstr ""
334
 
335
- #: classes/Views/Licensing.php:39 classes/Views/Settings.php:73
336
  #: classes/Views/ToggleAlerts.php:44
337
  msgid "Settings have been saved."
338
  msgstr ""
339
 
340
- #: classes/Views/Licensing.php:41 classes/Views/Settings.php:75
341
  #: classes/Views/ToggleAlerts.php:46
342
  msgid "Error: "
343
  msgstr ""
@@ -355,146 +291,170 @@ msgid "Inactive"
355
  msgstr ""
356
 
357
  #: classes/Views/Sandbox.php:11 classes/Views/Sandbox.php:19
358
- #: classes/Views/Settings.php:233
359
  msgid "Sandbox"
360
  msgstr ""
361
 
362
- #: classes/Views/Sandbox.php:191
363
  msgid "Ready."
364
  msgstr ""
365
 
366
- #: classes/Views/Settings.php:15 classes/Views/Settings.php:23
367
  msgid "Settings"
368
  msgstr ""
369
 
370
- #: classes/Views/Settings.php:86
371
  msgid "Security Alerts Pruning"
372
  msgstr ""
373
 
374
- #: classes/Views/Settings.php:89
375
  msgid "(eg: 1 month)"
376
  msgstr ""
377
 
378
- #: classes/Views/Settings.php:94
379
  msgid "Delete alerts older than"
380
  msgstr ""
381
 
382
- #: classes/Views/Settings.php:106
383
  msgid "(eg: 80)"
384
  msgstr ""
385
 
386
- #: classes/Views/Settings.php:111
387
  msgid "Keep up to"
388
  msgstr ""
389
 
390
- #: classes/Views/Settings.php:115
391
  msgid "alerts"
392
  msgstr ""
393
 
394
- #: classes/Views/Settings.php:121
 
 
 
 
 
 
 
 
 
 
 
 
395
  msgid "Alerts Dashboard Widget"
396
  msgstr ""
397
 
398
- #: classes/Views/Settings.php:127
399
  msgid "On"
400
  msgstr ""
401
 
402
- #: classes/Views/Settings.php:132
403
  msgid "Off"
404
  msgstr ""
405
 
406
- #: classes/Views/Settings.php:137
407
  msgid "Display a dashboard widget with the latest %d security alerts."
408
  msgstr ""
409
 
410
- #: classes/Views/Settings.php:145
411
  msgid "Can View Alerts"
412
  msgstr ""
413
 
414
- #: classes/Views/Settings.php:152
415
  msgid "Users and Roles in this list can view the security alerts"
416
  msgstr ""
417
 
418
- #: classes/Views/Settings.php:167
419
  msgid "Can Manage Plugin"
420
  msgstr ""
421
 
422
- #: classes/Views/Settings.php:174
423
  msgid "Users and Roles in this list can manage the plugin settings"
424
  msgstr ""
425
 
426
- #: classes/Views/Settings.php:189
 
 
 
 
 
 
 
 
 
 
 
 
427
  msgid "Refresh Audit View"
428
  msgstr ""
429
 
430
- #: classes/Views/Settings.php:195
431
  msgid "Automatic"
432
  msgstr ""
433
 
434
- #: classes/Views/Settings.php:197
435
  msgid "Refresh Audit View as soon as there are new events."
436
  msgstr ""
437
 
438
- #: classes/Views/Settings.php:201
439
  msgid "Manual"
440
  msgstr ""
441
 
442
- #: classes/Views/Settings.php:203
443
  msgid "Refresh Audit View only when page is reloaded."
444
  msgstr ""
445
 
446
- #: classes/Views/Settings.php:209
447
  msgid "Developer Options"
448
  msgstr ""
449
 
450
- #: classes/Views/Settings.php:217
451
  msgid ""
452
  "Only enable these options on testing, staging and development websites. "
453
  "Enabling any of the settings below on LIVE websites may cause unintended "
454
  "side-effects including degraded performance."
455
  msgstr ""
456
 
457
- #: classes/Views/Settings.php:221
458
  msgid "Data Inspector"
459
  msgstr ""
460
 
461
- #: classes/Views/Settings.php:222
462
  msgid "View data logged for each triggered alert."
463
  msgstr ""
464
 
465
- #: classes/Views/Settings.php:225
466
  msgid "PHP Errors"
467
  msgstr ""
468
 
469
- #: classes/Views/Settings.php:226
470
  msgid "Enables sensor for alerts generated from PHP."
471
  msgstr ""
472
 
473
- #: classes/Views/Settings.php:229
474
  msgid "Request Log"
475
  msgstr ""
476
 
477
- #: classes/Views/Settings.php:230
478
  msgid "Enables logging request to file."
479
  msgstr ""
480
 
481
- #: classes/Views/Settings.php:234
482
  msgid "Enables sandbox for testing PHP code."
483
  msgstr ""
484
 
485
- #: classes/Views/Settings.php:237
486
  msgid "Backtrace"
487
  msgstr ""
488
 
489
- #: classes/Views/Settings.php:238
490
  msgid "Log full backtrace for PHP-generated alerts."
491
  msgstr ""
492
 
493
- #: classes/Views/Settings.php:256
494
  msgid "Hide Plugin from Plugins Page"
495
  msgstr ""
496
 
497
- #: classes/Views/Settings.php:262
498
  msgid "Hide"
499
  msgstr ""
500
 
@@ -502,6 +462,14 @@ msgstr ""
502
  msgid "Enable/Disable Alerts"
503
  msgstr ""
504
 
 
 
 
 
 
 
 
 
505
  #: classes/Views/ToggleAlerts.php:71 classes/WidgetManager.php:38
506
  msgid "Description"
507
  msgstr ""
@@ -874,7 +842,7 @@ msgid "User modified a draft WordPress page"
874
  msgstr ""
875
 
876
  #: defaults.php:74
877
- msgid "Modified the draft page %PostTitle%. page ID is %PostID%"
878
  msgstr ""
879
 
880
  #: defaults.php:75
@@ -1553,7 +1521,7 @@ msgid ""
1553
  ">get_template_directory%"
1554
  msgstr ""
1555
 
1556
- #: wp-security-audit-log.php:157
1557
  msgid ""
1558
  "You are using a version of PHP that is older than %s, which is no longer "
1559
  "supported.<br/>Contact us on <a href=\"mailto:plugins@wpwhitesecurity.com"
2
  # This file is distributed under the same license as the WP Security Audit Log package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: WP Security Audit Log 1.2.7\n"
6
  "Report-Msgid-Bugs-To: http://wordpress.org/tag/wp-security-audit-log\n"
7
+ "POT-Creation-Date: 2014-09-26 09:43:13+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
136
  msgid "Professional WordPress security services provided by WP White Security"
137
  msgstr ""
138
 
139
+ #: classes/Views/AuditLog.php:22 classes/Views/AuditLog.php:32
140
  msgid "Audit Log Viewer"
141
  msgstr ""
142
 
143
+ #: classes/Views/AuditLog.php:46 classes/Views/Licensing.php:34
144
+ #: classes/Views/Settings.php:80 classes/Views/ToggleAlerts.php:29
145
  msgid "You do not have sufficient permissions to access this page."
146
  msgstr ""
147
 
148
+ #: classes/Views/AuditLog.php:66
149
  msgid "Please enter the number of alerts you would like to see on one page:"
150
  msgstr ""
151
 
152
+ #: classes/Views/AuditLog.php:67
153
  msgid "All Sites"
154
  msgstr ""
155
 
156
+ #: classes/Views/AuditLog.php:68
157
  msgid "No Results"
158
  msgstr ""
159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  #: classes/Views/Help.php:6 classes/Views/Help.php:14
161
  #: classes/Views/Help.php:25
162
  msgid "Help"
268
  msgid "Licensing"
269
  msgstr ""
270
 
271
+ #: classes/Views/Licensing.php:39 classes/Views/Settings.php:85
272
  #: classes/Views/ToggleAlerts.php:44
273
  msgid "Settings have been saved."
274
  msgstr ""
275
 
276
+ #: classes/Views/Licensing.php:41 classes/Views/Settings.php:87
277
  #: classes/Views/ToggleAlerts.php:46
278
  msgid "Error: "
279
  msgstr ""
291
  msgstr ""
292
 
293
  #: classes/Views/Sandbox.php:11 classes/Views/Sandbox.php:19
294
+ #: classes/Views/Settings.php:264
295
  msgid "Sandbox"
296
  msgstr ""
297
 
298
+ #: classes/Views/Sandbox.php:197
299
  msgid "Ready."
300
  msgstr ""
301
 
302
+ #: classes/Views/Settings.php:18 classes/Views/Settings.php:26
303
  msgid "Settings"
304
  msgstr ""
305
 
306
+ #: classes/Views/Settings.php:98
307
  msgid "Security Alerts Pruning"
308
  msgstr ""
309
 
310
+ #: classes/Views/Settings.php:101
311
  msgid "(eg: 1 month)"
312
  msgstr ""
313
 
314
+ #: classes/Views/Settings.php:106
315
  msgid "Delete alerts older than"
316
  msgstr ""
317
 
318
+ #: classes/Views/Settings.php:113
319
  msgid "(eg: 80)"
320
  msgstr ""
321
 
322
+ #: classes/Views/Settings.php:118
323
  msgid "Keep up to"
324
  msgstr ""
325
 
326
+ #: classes/Views/Settings.php:122
327
  msgid "alerts"
328
  msgstr ""
329
 
330
+ #: classes/Views/Settings.php:126
331
+ msgid "Next Scheduled Cleanup is in "
332
+ msgstr ""
333
+
334
+ #: classes/Views/Settings.php:130
335
+ msgid "(or %s)"
336
+ msgstr ""
337
+
338
+ #: classes/Views/Settings.php:131
339
+ msgid "Run Manually"
340
+ msgstr ""
341
+
342
+ #: classes/Views/Settings.php:137
343
  msgid "Alerts Dashboard Widget"
344
  msgstr ""
345
 
346
+ #: classes/Views/Settings.php:143
347
  msgid "On"
348
  msgstr ""
349
 
350
+ #: classes/Views/Settings.php:148
351
  msgid "Off"
352
  msgstr ""
353
 
354
+ #: classes/Views/Settings.php:153
355
  msgid "Display a dashboard widget with the latest %d security alerts."
356
  msgstr ""
357
 
358
+ #: classes/Views/Settings.php:161
359
  msgid "Can View Alerts"
360
  msgstr ""
361
 
362
+ #: classes/Views/Settings.php:168
363
  msgid "Users and Roles in this list can view the security alerts"
364
  msgstr ""
365
 
366
+ #: classes/Views/Settings.php:183
367
  msgid "Can Manage Plugin"
368
  msgstr ""
369
 
370
+ #: classes/Views/Settings.php:190
371
  msgid "Users and Roles in this list can manage the plugin settings"
372
  msgstr ""
373
 
374
+ #: classes/Views/Settings.php:205
375
+ msgid "Restrict Plugin Access"
376
+ msgstr ""
377
+
378
+ #: classes/Views/Settings.php:213
379
+ msgid ""
380
+ "By default all the administrators on this WordPress have access to manage "
381
+ "this plugin.<br/>By enabling this option only the users specified in the two "
382
+ "options above and your username will have access to view alerts and manage "
383
+ "this plugin."
384
+ msgstr ""
385
+
386
+ #: classes/Views/Settings.php:220
387
  msgid "Refresh Audit View"
388
  msgstr ""
389
 
390
+ #: classes/Views/Settings.php:226
391
  msgid "Automatic"
392
  msgstr ""
393
 
394
+ #: classes/Views/Settings.php:228
395
  msgid "Refresh Audit View as soon as there are new events."
396
  msgstr ""
397
 
398
+ #: classes/Views/Settings.php:232
399
  msgid "Manual"
400
  msgstr ""
401
 
402
+ #: classes/Views/Settings.php:234
403
  msgid "Refresh Audit View only when page is reloaded."
404
  msgstr ""
405
 
406
+ #: classes/Views/Settings.php:240
407
  msgid "Developer Options"
408
  msgstr ""
409
 
410
+ #: classes/Views/Settings.php:248
411
  msgid ""
412
  "Only enable these options on testing, staging and development websites. "
413
  "Enabling any of the settings below on LIVE websites may cause unintended "
414
  "side-effects including degraded performance."
415
  msgstr ""
416
 
417
+ #: classes/Views/Settings.php:252
418
  msgid "Data Inspector"
419
  msgstr ""
420
 
421
+ #: classes/Views/Settings.php:253
422
  msgid "View data logged for each triggered alert."
423
  msgstr ""
424
 
425
+ #: classes/Views/Settings.php:256
426
  msgid "PHP Errors"
427
  msgstr ""
428
 
429
+ #: classes/Views/Settings.php:257
430
  msgid "Enables sensor for alerts generated from PHP."
431
  msgstr ""
432
 
433
+ #: classes/Views/Settings.php:260
434
  msgid "Request Log"
435
  msgstr ""
436
 
437
+ #: classes/Views/Settings.php:261
438
  msgid "Enables logging request to file."
439
  msgstr ""
440
 
441
+ #: classes/Views/Settings.php:265
442
  msgid "Enables sandbox for testing PHP code."
443
  msgstr ""
444
 
445
+ #: classes/Views/Settings.php:268
446
  msgid "Backtrace"
447
  msgstr ""
448
 
449
+ #: classes/Views/Settings.php:269
450
  msgid "Log full backtrace for PHP-generated alerts."
451
  msgstr ""
452
 
453
+ #: classes/Views/Settings.php:287
454
  msgid "Hide Plugin from Plugins Page"
455
  msgstr ""
456
 
457
+ #: classes/Views/Settings.php:293
458
  msgid "Hide"
459
  msgstr ""
460
 
462
  msgid "Enable/Disable Alerts"
463
  msgstr ""
464
 
465
+ #: classes/Views/ToggleAlerts.php:69
466
+ msgid "Code"
467
+ msgstr ""
468
+
469
+ #: classes/Views/ToggleAlerts.php:70
470
+ msgid "Type"
471
+ msgstr ""
472
+
473
  #: classes/Views/ToggleAlerts.php:71 classes/WidgetManager.php:38
474
  msgid "Description"
475
  msgstr ""
842
  msgstr ""
843
 
844
  #: defaults.php:74
845
+ msgid "Modified the draft page %PostTitle%. Page ID is %PostID%"
846
  msgstr ""
847
 
848
  #: defaults.php:75
1521
  ">get_template_directory%"
1522
  msgstr ""
1523
 
1524
+ #: wp-security-audit-log.php:169
1525
  msgid ""
1526
  "You are using a version of PHP that is older than %s, which is no longer "
1527
  "supported.<br/>Contact us on <a href=\"mailto:plugins@wpwhitesecurity.com"
readme.txt CHANGED
@@ -6,8 +6,8 @@ License: GPLv3
6
  License URI: http://www.gnu.org/licenses/gpl.html
7
  Tags: wordpress security plugin, wordpress security audit log, audit log, wordpress 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
8
  Requires at least: 3.6
9
- Tested up to: 4.0
10
- Stable tag: 1.2.6
11
 
12
  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.
13
 
@@ -131,6 +131,18 @@ Yes, WP Security Audit Log works on WordPress Multisite networks, i.e. it can mo
131
 
132
  == Changelog ==
133
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  = 1.2.6 (2014-08-20) =
135
  * Improvements
136
  * Several performance improvements and tweaks applied
6
  License URI: http://www.gnu.org/licenses/gpl.html
7
  Tags: wordpress security plugin, wordpress security audit log, audit log, wordpress 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
8
  Requires at least: 3.6
9
+ Tested up to: 4
10
+ Stable tag: 1.2.7
11
 
12
  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.
13
 
131
 
132
  == Changelog ==
133
 
134
+ = 1.2.7 (2014-09-26) =
135
+ * New Feature
136
+ * New option "Restrict Plugin Access" that allows WordPress administrators to further restrict access to the plugin and the WordPress security alerts
137
+
138
+ * Improvements
139
+ * Updated the Audit Log Viewer backend to retriev WordPress security alerts much faster and consume less resources on large websites
140
+ * Moved the Audit Log plugin menu entry underneath the dashboard entry for better access
141
+ * Several minor enhancements to the plugin to perform better on large WordPress installations
142
+
143
+ * Bug Fixes
144
+ * Fixed an uncaught exception with Logout Alert 1001 [support ticket](https://wordpress.org/support/topic/uncaught-exception-2)
145
+
146
  = 1.2.6 (2014-08-20) =
147
  * Improvements
148
  * Several performance improvements and tweaks applied
wp-security-audit-log.php CHANGED
@@ -4,7 +4,7 @@ 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: 1.2.6
8
  Text Domain: wp-security-audit-log
9
  Author URI: http://www.wpwhitesecurity.com/
10
  License: GPL2
@@ -78,6 +78,12 @@ class WpSecurityAuditLog {
78
  */
79
  public $licensing;
80
 
 
 
 
 
 
 
81
  /**
82
  * Contains a list of cleanup callbacks.
83
  * @var callable[]
@@ -104,6 +110,10 @@ class WpSecurityAuditLog {
104
  * Initialize plugin.
105
  */
106
  public function __construct(){
 
 
 
 
107
  // load autoloader and register base paths
108
  require_once('classes/Autoloader.php');
109
  $this->autoloader = new WSAL_Autoloader($this);
@@ -138,7 +148,9 @@ class WpSecurityAuditLog {
138
  load_plugin_textdomain('wp-security-audit-log', false, basename( dirname( __FILE__ ) ) . '/languages/');
139
 
140
  // tell the world we've just finished loading
 
141
  do_action('wsal_init', $this);
 
142
  }
143
 
144
  /**
@@ -174,8 +186,9 @@ class WpSecurityAuditLog {
174
  // if system wasn't installed, try migration now
175
  if (!$PreInstalled && $this->CanMigrate()) $this->Migrate();
176
 
177
- // install cleanup hook
178
- wp_schedule_event(0, 'hourly', 'wsal_cleanup');
 
179
  }
180
 
181
  /**
@@ -201,7 +214,7 @@ class WpSecurityAuditLog {
201
  */
202
  public function Uninstall(){
203
  WSAL_DB_ActiveRecord::UninstallAll();
204
- wp_unschedule_event(0, 'wsal_cleanup');
205
  }
206
 
207
  /**
@@ -356,8 +369,10 @@ class WpSecurityAuditLog {
356
  * Run cleanup routines.
357
  */
358
  public function CleanUp(){
359
- foreach($this->_cleanup_hooks as $hook)
360
- call_user_func($hook);
 
 
361
  }
362
 
363
  /**
@@ -417,16 +432,32 @@ class WpSecurityAuditLog {
417
  return plugin_basename(__FILE__);
418
  }
419
 
 
 
 
 
 
 
 
 
 
420
  // </editor-fold>
421
  }
422
 
 
 
 
 
423
  add_action('plugins_loaded', array(WpSecurityAuditLog::GetInstance(), 'Load'));
424
 
425
  // Load extra files
426
- require_once('defaults.php');
427
 
428
  // Start listening to events
429
  WpSecurityAuditLog::GetInstance()->sensors->HookEvents();
430
 
 
 
 
431
  // Create & Run the plugin
432
  return WpSecurityAuditLog::GetInstance();
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.2.7
8
  Text Domain: wp-security-audit-log
9
  Author URI: http://www.wpwhitesecurity.com/
10
  License: GPL2
78
  */
79
  public $licensing;
80
 
81
+ /**
82
+ * Simple profiler.
83
+ * @var WSAL_SimpleProfiler
84
+ */
85
+ public $profiler;
86
+
87
  /**
88
  * Contains a list of cleanup callbacks.
89
  * @var callable[]
110
  * Initialize plugin.
111
  */
112
  public function __construct(){
113
+ // profiler has to be loaded manually
114
+ require_once('classes/SimpleProfiler.php');
115
+ $this->profiler = new WSAL_SimpleProfiler();
116
+
117
  // load autoloader and register base paths
118
  require_once('classes/Autoloader.php');
119
  $this->autoloader = new WSAL_Autoloader($this);
148
  load_plugin_textdomain('wp-security-audit-log', false, basename( dirname( __FILE__ ) ) . '/languages/');
149
 
150
  // tell the world we've just finished loading
151
+ $s = $this->profiler->Start('WSAL Init Hook');
152
  do_action('wsal_init', $this);
153
+ $s->Stop();
154
  }
155
 
156
  /**
186
  // if system wasn't installed, try migration now
187
  if (!$PreInstalled && $this->CanMigrate()) $this->Migrate();
188
 
189
+ // install cleanup hook (remove older one if it exists)
190
+ wp_clear_scheduled_hook('wsal_cleanup');
191
+ wp_schedule_event(current_time('timestamp') + 600, 'hourly', 'wsal_cleanup');
192
  }
193
 
194
  /**
214
  */
215
  public function Uninstall(){
216
  WSAL_DB_ActiveRecord::UninstallAll();
217
+ wp_clear_scheduled_hook('wsal_cleanup');
218
  }
219
 
220
  /**
369
  * Run cleanup routines.
370
  */
371
  public function CleanUp(){
372
+ $s = $this->profiler->Start('Clean Up');
373
+ //foreach($this->_cleanup_hooks as $hook)
374
+ // call_user_func($hook);
375
+ $s->Stop();
376
  }
377
 
378
  /**
432
  return plugin_basename(__FILE__);
433
  }
434
 
435
+ /**
436
+ * Load default configuration / data.
437
+ */
438
+ public function LoadDefaults(){
439
+ $s = $this->profiler->Start('Load Defaults');
440
+ require_once('defaults.php');
441
+ $s->Stop();
442
+ }
443
+
444
  // </editor-fold>
445
  }
446
 
447
+ // Profile WSAL load time
448
+ $s = WpSecurityAuditLog::GetInstance()->profiler->Start('WSAL Init');
449
+
450
+ // Begin load sequence
451
  add_action('plugins_loaded', array(WpSecurityAuditLog::GetInstance(), 'Load'));
452
 
453
  // Load extra files
454
+ WpSecurityAuditLog::GetInstance()->LoadDefaults();
455
 
456
  // Start listening to events
457
  WpSecurityAuditLog::GetInstance()->sensors->HookEvents();
458
 
459
+ // End profile snapshot
460
+ $s->Stop();
461
+
462
  // Create & Run the plugin
463
  return WpSecurityAuditLog::GetInstance();