Simple History - Version 2.14

Version Description

(April 2016) =

  • Added support for plugin Duplicate Post. Now when a user clones a post or page you will se this in the history log, with links to both the original post and the new copy.
  • Removed log level info from title in RSS feed
  • Make date dropdown less "jumpy" when loading page (due to select element switching to Select2)
  • Only add filters for plugin Limit Login Attempts if plugin is active. This fixes problem with Limit Login Attempts Reloaded and possibly other forks of the plugin.
  • Debug page now displays installed plugins.
Download this release

Release Info

Developer eskapism
Plugin Icon 128x128 Simple History
Version 2.14
Comparing to
See all releases

Code changes from version 2.13 to 2.14

dropins/SimpleHistoryFilterDropin.css CHANGED
@@ -10,6 +10,13 @@
10
  transition: all .25s ease-out;
11
  }
12
 
 
 
 
 
 
 
 
13
  .SimpleHistory--isLoaded .SimpleHistory__filters {
14
  opacity: 1;
15
  }
@@ -33,6 +40,7 @@
33
  .SimpleHistory__filters__filterLabel {
34
  display: inline-block;
35
  width: 150px;
 
36
  }
37
 
38
  .SimpleHistory__filters__filterSubmitWrap {
@@ -52,6 +60,20 @@
52
 
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  /* always label as blocks on dashboard because we don't know the width beacuse of columns */
56
  .postbox .SimpleHistory__filters__filterLabel {
57
  display: block;
@@ -61,12 +83,6 @@
61
  margin-left: 0;
62
  }
63
 
64
- /* set height on date input or it will "jump" during page load */
65
- .wp-admin select[multiple].SimpleHistory__filters__filter--date {
66
- height: 2.25em;
67
- overflow: hidden;
68
- }
69
-
70
  /**
71
  * Search results in filter
72
  */
10
  transition: all .25s ease-out;
11
  }
12
 
13
+ .SimpleHistory__filters__filter--date {
14
+ }
15
+
16
+ .SimpleHistory__filters__filter--date.select2-container {
17
+ }
18
+
19
+
20
  .SimpleHistory--isLoaded .SimpleHistory__filters {
21
  opacity: 1;
22
  }
40
  .SimpleHistory__filters__filterLabel {
41
  display: inline-block;
42
  width: 150px;
43
+ vertical-align: middle;
44
  }
45
 
46
  .SimpleHistory__filters__filterSubmitWrap {
60
 
61
  }
62
 
63
+ @media (min-width: 600px) {
64
+ /* prevent "jump" during page load because of select elm changing to select2 */
65
+ .SimpleHistory__filters__filterRow {
66
+ height: 41px;
67
+ line-height: 41px;
68
+ }
69
+
70
+ .wp-admin select[multiple].SimpleHistory__filters__filter--date {
71
+ height: 2.25em;
72
+ overflow: hidden;
73
+ }
74
+ }
75
+
76
+
77
  /* always label as blocks on dashboard because we don't know the width beacuse of columns */
78
  .postbox .SimpleHistory__filters__filterLabel {
79
  display: block;
83
  margin-left: 0;
84
  }
85
 
 
 
 
 
 
 
86
  /**
87
  * Search results in filter
88
  */
dropins/SimpleHistoryFilterDropin.php CHANGED
@@ -163,7 +163,7 @@ class SimpleHistoryFilterDropin {
163
 
164
  ?>
165
 
166
- <p data-debug-daysAndPages='<?php echo json_encode( $arr_days_and_pages ) ?>'>
167
 
168
  <label class="SimpleHistory__filters__filterLabel"><?php _ex("Dates:", "Filter label", "simple-history") ?></label>
169
 
163
 
164
  ?>
165
 
166
+ <p class="SimpleHistory__filters__filterRow" data-debug-daysAndPages='<?php echo json_encode( $arr_days_and_pages ) ?>'>
167
 
168
  <label class="SimpleHistory__filters__filterLabel"><?php _ex("Dates:", "Filter label", "simple-history") ?></label>
169
 
dropins/SimpleHistoryRSSDropin.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
 
3
- defined( 'ABSPATH' ) or die();
4
 
5
  /*
6
  Dropin Name: Global RSS Feed
@@ -11,434 +11,435 @@ Author: Pär Thernström
11
  /**
12
  * Simple History RSS Feed drop-in
13
  */
14
- class SimpleHistoryRSSDropin {
15
-
16
- function __construct($sh) {
17
-
18
- $this->sh = $sh;
19
-
20
- if ( ! function_exists('get_editable_roles') ) {
21
- require_once( ABSPATH . '/wp-admin/includes/user.php' );
22
- }
23
-
24
- //Check the status of the RSS feed
25
- $this->is_rss_enabled();
26
-
27
- // Generate a rss secret, if it does not exist
28
- if ( ! get_option("simple_history_rss_secret") ) {
29
- $this->update_rss_secret();
30
- }
31
-
32
- add_action( 'init', array($this, 'check_for_rss_feed_request') );
33
-
34
- // Add settings with prio 11 so it' added after the main Simple History settings
35
- add_action( 'admin_menu', array($this, 'add_settings'), 11 );
36
-
37
- }
38
-
39
- /**
40
- * Add settings for the RSS feed
41
- * + also regenerates the secret if requested
42
- */
43
- public function add_settings() {
44
-
45
- //we register a setting to keep track of the RSS feed status (enabled/disabled)
46
- register_setting( SimpleHistory::SETTINGS_GENERAL_OPTION_GROUP, 'simple_history_enable_rss_feed', array($this, 'update_rss_status') );
47
-
48
- /**
49
- * Start new section for RSS feed
50
- */
51
- $settings_section_rss_id = "simple_history_settings_section_rss";
52
-
53
- add_settings_section(
54
- $settings_section_rss_id,
55
- _x("RSS feed", "rss settings headline", "simple-history"), // No title __("General", "simple-history"),
56
- array($this, "settings_section_output"),
57
- SimpleHistory::SETTINGS_MENU_SLUG // same slug as for options menu page
58
- );
59
-
60
- // Enable/Disabled RSS feed
61
- add_settings_field(
62
- "simple_history_enable_rss_feed",
63
- __("Enable", "simple-history"),
64
- array($this, "settings_field_rss_enable"),
65
- SimpleHistory::SETTINGS_MENU_SLUG,
66
- $settings_section_rss_id
67
- );
68
-
69
- //if RSS is activated we display other fields
70
- if($this->is_rss_enabled()){
71
-
72
- // RSS address
73
- add_settings_field(
74
- "simple_history_rss_feed",
75
- __("Address", "simple-history"),
76
- array($this, "settings_field_rss"),
77
- SimpleHistory::SETTINGS_MENU_SLUG,
78
- $settings_section_rss_id
79
- );
80
-
81
- // Regnerate address
82
- add_settings_field(
83
- "simple_history_rss_feed_regenerate_secret",
84
- __("Regenerate", "simple-history"),
85
- array($this, "settings_field_rss_regenerate"),
86
- SimpleHistory::SETTINGS_MENU_SLUG,
87
- $settings_section_rss_id
88
- );
89
-
90
- }
91
-
92
- // Create new RSS secret
93
- $create_new_secret = false;
94
- $create_secret_nonce_name = "simple_history_rss_secret_regenerate_nonce";
95
-
96
- if ( isset( $_GET[$create_secret_nonce_name] ) && wp_verify_nonce( $_GET[$create_secret_nonce_name], 'simple_history_rss_update_secret')) {
97
-
98
- $create_new_secret = true;
99
- $this->update_rss_secret();
100
-
101
- // Add updated-message and store in transient and then redirect
102
- // This is the way options.php does it.
103
- $msg = __("Created new secret RSS address", 'simple-history');
104
- add_settings_error( "simple_history_rss_feed_regenerate_secret", "simple_history_rss_feed_regenerate_secret", $msg, "updated" );
105
- set_transient('settings_errors', get_settings_errors(), 30);
106
-
107
- $goback = esc_url_raw( add_query_arg( 'settings-updated', 'true', wp_get_referer() ) );
108
- wp_redirect( $goback );
109
- exit;
110
-
111
- }
112
-
113
- } // settings
114
-
115
- /**
116
- * Check if RSS feed is enabled or disabled
117
- */
118
- function is_rss_enabled() {
119
-
120
- // User has never used the plugin we disable RSS feed
121
- if ( get_option("simple_history_rss_secret") === false && get_option("simple_history_enable_rss_feed") === false ) {
122
- //We disable RSS by default, we use 0/1 to prevent fake disabled with bools from functions returning false for unset
123
- update_option("simple_history_enable_rss_feed" , "0" );
124
- }
125
- // User was using the plugin before RSS feed became disabled by default
126
- // We activate RSS to prevent a "breaking change"
127
- else if(get_option("simple_history_enable_rss_feed") === false){
128
- update_option("simple_history_enable_rss_feed" , "1" );
129
- return true;
130
- }
131
- else if( get_option("simple_history_enable_rss_feed") === "1" ){
132
- return true;
133
- }
134
-
135
- return false;
136
-
137
- }
138
-
139
- /**
140
- * Output for settings field that show current RSS address
141
- */
142
- function settings_field_rss_enable() {
143
-
144
- ?>
145
-
146
- <input value="1" type="checkbox" id="simple_history_enable_rss_feed" name="simple_history_enable_rss_feed" <?php checked( $this->is_rss_enabled(), 1 ); ?> />
147
- <label for="simple_history_enable_rss_feed"><?php _e( "Enable RSS feed", 'simple-history' )?></label>
148
-
149
- <?php
150
-
151
- }
152
-
153
- /**
154
- * Sanitize RSS enabled/disabled status on update settings
155
- */
156
- function update_rss_status($field) {
157
-
158
- if( $field === "1" ){
159
- return "1";
160
- }
161
-
162
- return "0";
163
-
164
- }
165
-
166
-
167
- /**
168
- * Check if current request is a request for the RSS feed
169
- */
170
- function check_for_rss_feed_request() {
171
-
172
- // check for RSS
173
- // don't know if this is the right way to do this, but it seems to work!
174
- if ( isset( $_GET["simple_history_get_rss"] ) ) {
175
-
176
- $this->output_rss();
177
- exit;
178
-
179
- }
180
-
181
- }
182
-
183
- /**
184
- * Modify capability check so all users reading rss feed (logged in or not) can read all loggers
185
- */
186
- public function on_can_read_single_logger( $user_can_read_logger, $logger_instance, $user_id ) {
187
-
188
- $user_can_read_logger = true;
189
-
190
- return $user_can_read_logger;
191
-
192
- }
193
-
194
- /**
195
- * Output RSS
196
- */
197
- function output_rss() {
198
-
199
- $rss_secret_option = get_option("simple_history_rss_secret");
200
- $rss_secret_get = isset( $_GET["rss_secret"] ) ? $_GET["rss_secret"] : "";
201
-
202
- if ( empty( $rss_secret_option ) || empty( $rss_secret_get ) ) {
203
- die();
204
- }
205
-
206
- $rss_show = true;
207
- $rss_show = apply_filters("simple_history/rss_feed_show", $rss_show);
208
- if( ! $rss_show || ! $this->is_rss_enabled() ) {
209
- wp_die( 'Nothing here.' );
210
- }
211
-
212
- header("Content-Type: text/xml; charset=utf-8");
213
- echo '<?xml version="1.0" encoding="UTF-8"?>';
214
- $self_link = $this->get_rss_address();
215
-
216
- if ( $rss_secret_option === $rss_secret_get ) {
217
-
218
- ?>
219
- <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
220
- <channel>
221
- <title><![CDATA[<?php printf(__("History for %s", 'simple-history'), get_bloginfo("name")) ?>]]></title>
222
- <description><![CDATA[<?php printf(__("WordPress History for %s", 'simple-history'), get_bloginfo("name")) ?>]]></description>
223
- <link><?php echo get_bloginfo("url") ?></link>
224
- <atom:link href="<?php echo $self_link; ?>" rel="self" type="application/atom+xml" />
225
- <?php
226
-
227
- // Override capability check: if you have a valid rss_secret_key you can read it all
228
- $action_tag = "simple_history/loggers_user_can_read/can_read_single_logger";
229
- add_action( $action_tag, array($this, "on_can_read_single_logger"), 10, 3 );
230
-
231
- // Modify header time output so it does not show relative date or time ago-format
232
- // Because we don't know when a user reads the RSS feed, time ago format may be very inaccurate
233
- add_action("simple_history/header_just_now_max_time", "__return_zero");
234
- add_action("simple_history/header_time_ago_max_time", "__return_zero");
235
-
236
- // Get log rows
237
- $args = array(
238
- "posts_per_page" => 10
239
- );
240
-
241
- $args = apply_filters("simple_history/rss_feed_args", $args);
242
-
243
- $logQuery = new SimpleHistoryLogQuery();
244
- $queryResults = $logQuery->query($args);
245
-
246
- // Remove capability override after query is done
247
- // remove_action( $action_tag, array($this, "on_can_read_single_logger") );
248
-
249
- foreach ( $queryResults["log_rows"] as $row ) {
250
-
251
- $header_output = $this->sh->getLogRowHeaderOutput( $row );
252
- $text_output = $this->sh->getLogRowPlainTextOutput( $row );
253
- $details_output = $this->sh->getLogRowDetailsOutput( $row );
254
-
255
- // http://cyber.law.harvard.edu/rss/rss.html#ltguidgtSubelementOfLtitemgt
256
- //$item_guid = home_url() . "?SimpleHistoryGuid=" . $row->id;
257
- $item_guid = esc_url( add_query_arg("SimpleHistoryGuid", $row->id, home_url()) );
258
- $item_link = esc_url( add_query_arg("SimpleHistoryGuid", $row->id, home_url()) );
259
-
260
- /**
261
- * Filter the guid/link URL used in RSS feed.
262
- * Link will be esc_url'ed by simple history, so no need to do that in your filter
263
- *
264
- * @since 2.0.23
265
- *
266
- * @param string $item_guid link.
267
- * @param array $row
268
- */
269
- $item_link = apply_filters("simple_history/rss_item_link", $item_link, $row);
270
- $item_link = esc_url($item_link);
271
-
272
- $item_title = $this->sh->getLogLevelTranslated( $row->level ) . ": " . wp_kses( $text_output, array() );
273
-
274
- $level_output = sprintf( __('Severity level: %1$s'), $this->sh->getLogLevelTranslated( $row->level ));
275
-
276
- ?>
277
- <item>
278
- <title><![CDATA[<?php echo $item_title; ?>]]></title>
279
- <description><![CDATA[
280
- <p><?php echo $header_output ?></p>
281
- <p><?php echo $text_output ?></p>
282
- <div><?php echo $details_output ?></div>
283
- <p><?php echo $level_output ?></p>
284
- <?php
285
- $occasions = $row->subsequentOccasions - 1;
286
- if ( $occasions ) {
287
- printf( _n( '+%1$s occasion', '+%1$s occasions', $occasions, 'simple-history' ), $occasions );
288
- }
289
- ?>
290
- ]]></description>
291
- <?php
292
- // author must be email to validate, but the field is optional, so we skip it
293
- /* <author><?php echo $row->initiator ?></author> */
294
- ?>
295
- <pubDate><?php echo date("D, d M Y H:i:s", strtotime($row->date)) ?> GMT</pubDate>
296
- <guid isPermaLink="false"><![CDATA[<?php echo $item_guid ?>]]></guid>
297
- <link><![CDATA[<?php echo $item_link ?>]]></link>
298
- </item>
299
- <?php
300
- /*
301
- [0] =&gt; stdClass Object
302
- (
303
- [id] =&gt; 27324
304
- [logger] =&gt; SimplePluginLogger
305
- [level] =&gt; info
306
- [date] =&gt; 2014-10-15 06:50:01
307
- [message] =&gt; Updated plugin &quot;{plugin_name}&quot; from {plugin_prev_version} to {plugin_version}
308
- [type] =&gt;
309
- [initiator] =&gt; wp_user
310
- [occasionsID] =&gt; 75e8aeab3e43b37f8a458f3744c4995f
311
- [subsequentOccasions] =&gt; 1
312
- [rep] =&gt; 1
313
- [repeated] =&gt; 1
314
- [occasionsIDType] =&gt; 75e8aeab3e43b37f8a458f3744c4995f
315
- [context] =&gt; Array
316
- (
317
- [plugin_slug] =&gt; google-analytics-for-wordpress
318
- [plugin_name] =&gt; Google Analytics by Yoast
319
- [plugin_title] =&gt; &lt;a href=&quot;https://yoast.com/wordpress/plugins/google-analytics/#utm_source=wordpress&amp;#038;utm_medium=plugin&amp;#038;utm_campaign=wpgaplugin&amp;#038;utm_content=v504&quot;&gt;Google Analytics by Yoast&lt;/a&gt;
320
- [plugin_description] =&gt; This plugin makes it simple to add Google Analytics to your WordPress blog, adding lots of features, eg. error page, search result and automatic clickout and download tracking. &lt;cite&gt;By &lt;a href=&quot;https://yoast.com/&quot;&gt;Team Yoast&lt;/a&gt;.&lt;/cite&gt;
321
- [plugin_author] =&gt; &lt;a href=&quot;https://yoast.com/&quot;&gt;Team Yoast&lt;/a&gt;
322
- [plugin_version] =&gt; 5.0.7
323
- [plugin_url] =&gt; https://yoast.com/wordpress/plugins/google-analytics/#utm_source=wordpress&amp;#038;utm_medium=plugin&amp;#038;utm_campaign=wpgaplugin&amp;#038;utm_content=v504
324
- [plugin_update_info_plugin] =&gt; google-analytics-for-wordpress/googleanalytics.php
325
- [plugin_update_info_package] =&gt; https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.5.0.7.zip
326
- [plugin_prev_version] =&gt; 5.0.6
327
- [_message_key] =&gt; plugin_bulk_updated
328
- [_user_id] =&gt; 1
329
- [_user_login] =&gt; admin
330
- [_user_email] =&gt; par.thernstrom@gmail.com
331
- [_server_remote_addr] =&gt; ::1
332
- [_server_http_referer] =&gt; http://playground-root.ep/wp-admin/update-core.php?action=do-plugin-upgrade
333
- )
334
-
335
- )
336
- */
337
-
338
- }
339
-
340
- ?>
341
- </channel>
342
- </rss>
343
- <?php
344
- } else {
345
-
346
- // RSS secret was not ok
347
- ?>
348
- <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
349
- <channel>
350
- <title><?php printf(__("History for %s", 'simple-history'), get_bloginfo("name")) ?></title>
351
- <description><?php printf(__("WordPress History for %s", 'simple-history'), get_bloginfo("name")) ?></description>
352
- <link><?php echo home_url() ?></link>
353
- <item>
354
- <title><?php _e("Wrong RSS secret", 'simple-history')?></title>
355
- <description><?php _e("Your RSS secret for Simple History RSS feed is wrong. Please see WordPress settings for current link to the RSS feed.", 'simple-history')?></description>
356
- <pubDate><?php echo date("D, d M Y H:i:s", time()) ?> GMT</pubDate>
357
- <guid><?php echo home_url() . "?SimpleHistoryGuid=wrong-secret" ?></guid>
358
- </item>
359
- </channel>
360
- </rss>
361
- <?php
362
-
363
- }
364
-
365
- } // rss
366
-
367
-
368
- /**
369
- * Create a new RSS secret
370
- *
371
- * @return string new secret
372
- */
373
- function update_rss_secret() {
374
-
375
- $rss_secret = "";
376
-
377
- for ($i=0; $i<20; $i++) {
378
- $rss_secret .= chr(rand(97,122));
379
- }
380
-
381
- update_option("simple_history_rss_secret", $rss_secret);
382
-
383
- return $rss_secret;
384
- }
385
-
386
- /**
387
- * Output for settings field that show current RSS address
388
- */
389
- function settings_field_rss() {
390
-
391
- $rss_address = $this->get_rss_address();
392
-
393
- echo "<p><code><a href='$rss_address'>$rss_address</a></code></p>";
394
-
395
- }
396
-
397
- /**
398
- * Output for settings field that regenerates the RSS adress/secret
399
- */
400
- function settings_field_rss_regenerate() {
401
-
402
- $update_link = esc_url( add_query_arg("", "") );
403
- $update_link = wp_nonce_url( $update_link, "simple_history_rss_update_secret", "simple_history_rss_secret_regenerate_nonce" );
404
-
405
- echo "<p>";
406
- _e("You can generate a new address for the RSS feed. This is useful if you think that the address has fallen into the wrong hands.", 'simple-history');
407
- echo "</p>";
408
- echo "<p>";
409
- printf( '<a class="button" href="%1$s">%2$s</a>', $update_link, __('Generate new address', "simple-history") );
410
-
411
- echo "</p>";
412
-
413
- }
414
-
415
-
416
- /**
417
- * Get the URL to the RSS feed
418
- *
419
- * @return string URL
420
- */
421
- function get_rss_address() {
422
-
423
- $rss_secret = get_option("simple_history_rss_secret");
424
- $rss_address = add_query_arg(array("simple_history_get_rss" => "1", "rss_secret" => $rss_secret), get_bloginfo("url") . "/");
425
- $rss_address = esc_url( $rss_address );
426
- // $rss_address = htmlspecialchars($rss_address, ENT_COMPAT, "UTF-8");
427
-
428
- return $rss_address;
429
-
430
- }
431
-
432
- /**
433
- * Content for section intro. Leave it be, even if empty.
434
- * Called from add_sections_setting.
435
- */
436
- function settings_section_output() {
437
-
438
- echo "<p>";
439
- _e("Simple History has a RSS feed which you can subscribe to and receive log updates. Make sure you only share the feed with people you trust, since it can contain sensitive or confidential information.", 'simple-history');
440
- echo "</p>";
441
-
442
- }
443
-
 
444
  } // end rss class
1
  <?php
2
 
3
+ // defined('ABSPATH') or die();
4
 
5
  /*
6
  Dropin Name: Global RSS Feed
11
  /**
12
  * Simple History RSS Feed drop-in
13
  */
14
+ class SimpleHistoryRSSDropin
15
+ {
16
+
17
+ public function __construct($sh)
18
+ {
19
+
20
+ $this->sh = $sh;
21
+
22
+ if (! function_exists('get_editable_roles')) {
23
+ require_once(ABSPATH . '/wp-admin/includes/user.php');
24
+ }
25
+
26
+ //Check the status of the RSS feed
27
+ $this->isRssEnabled();
28
+
29
+ // Generate a rss secret, if it does not exist
30
+ if (! get_option("simple_history_rss_secret")) {
31
+ $this->updateRssSecret();
32
+ }
33
+
34
+ add_action('init', array($this, 'checkForRssFeedRequest'));
35
+
36
+ // Add settings with prio 11 so it' added after the main Simple History settings
37
+ add_action('admin_menu', array($this, 'addSettings'), 11);
38
+ }
39
+
40
+ /**
41
+ * Add settings for the RSS feed
42
+ * + also regenerates the secret if requested
43
+ */
44
+ public function addSettings()
45
+ {
46
+
47
+ //we register a setting to keep track of the RSS feed status (enabled/disabled)
48
+ register_setting(
49
+ SimpleHistory::SETTINGS_GENERAL_OPTION_GROUP,
50
+ 'simple_history_enable_rss_feed',
51
+ array($this, 'public updateRssStatus'
52
+ )
53
+ );
54
+ /**
55
+ * Start new section for RSS feed
56
+ */
57
+ $settings_section_rss_id = "simple_history_settings_section_rss";
58
+
59
+ add_settings_section(
60
+ $settings_section_rss_id,
61
+ _x("RSS feed", "rss settings headline", "simple-history"), // No title __("General", "simple-history"),
62
+ array($this, "settingsSectionOutput"),
63
+ SimpleHistory::SETTINGS_MENU_SLUG // same slug as for options menu page
64
+ );
65
+
66
+ // Enable/Disabled RSS feed
67
+ add_settings_field(
68
+ "simple_history_enable_rss_feed",
69
+ __("Enable", "simple-history"),
70
+ array($this, "settingsFieldRssEnable"),
71
+ SimpleHistory::SETTINGS_MENU_SLUG,
72
+ $settings_section_rss_id
73
+ );
74
+
75
+ //if RSS is activated we display other fields
76
+ if ($this->isRssEnabled()) {
77
+ // RSS address
78
+ add_settings_field(
79
+ "simple_history_rss_feed",
80
+ __("Address", "simple-history"),
81
+ array($this, "settingsFieldRss"),
82
+ SimpleHistory::SETTINGS_MENU_SLUG,
83
+ $settings_section_rss_id
84
+ );
85
+
86
+ // Regnerate address
87
+ add_settings_field(
88
+ "simple_history_rss_feed_regenerate_secret",
89
+ __("Regenerate", "simple-history"),
90
+ array($this, "settingsFieldRssRegenerate"),
91
+ SimpleHistory::SETTINGS_MENU_SLUG,
92
+ $settings_section_rss_id
93
+ );
94
+ }
95
+
96
+ // Create new RSS secret
97
+ $create_new_secret = false;
98
+ $create_secret_nonce_name = "simple_history_rss_secret_regenerate_nonce";
99
+ $createNonceOk = isset($_GET[$create_secret_nonce_name]) && wp_verify_nonce($_GET[$create_secret_nonce_name], 'simple_history_rss_update_secret');
100
+
101
+ if ($createNonceOk) {
102
+ $create_new_secret = true;
103
+ $this->updateRssSecret();
104
+
105
+ // Add updated-message and store in transient and then redirect
106
+ // This is the way options.php does it.
107
+ $msg = __("Created new secret RSS address", 'simple-history');
108
+ add_settings_error("simple_history_rss_feed_regenerate_secret", "simple_history_rss_feed_regenerate_secret", $msg, "updated");
109
+ set_transient('settings_errors', get_settings_errors(), 30);
110
+
111
+ $goback = esc_url_raw(add_query_arg('settings-updated', 'true', wp_get_referer()));
112
+ wp_redirect($goback);
113
+ exit;
114
+ }
115
+ } // settings
116
+
117
+ /**
118
+ * Check if RSS feed is enabled or disabled
119
+ */
120
+ public function isRssEnabled()
121
+ {
122
+
123
+ // User has never used the plugin we disable RSS feed
124
+ if (get_option("simple_history_rss_secret") === false && get_option("simple_history_enable_rss_feed") === false) {
125
+ //We disable RSS by default, we use 0/1 to prevent fake disabled with bools from functions returning false for unset
126
+ update_option("simple_history_enable_rss_feed", "0");
127
+ } elseif (get_option("simple_history_enable_rss_feed") === false) {
128
+ // User was using the plugin before RSS feed became disabled by default
129
+ // We activate RSS to prevent a "breaking change"
130
+ update_option("simple_history_enable_rss_feed", "1");
131
+ return true;
132
+ } elseif (get_option("simple_history_enable_rss_feed") === "1") {
133
+ return true;
134
+ }
135
+
136
+ return false;
137
+ }
138
+
139
+ /**
140
+ * Output for settings field that show current RSS address
141
+ */
142
+ public function settingsFieldRssEnable()
143
+ {
144
+ ?>
145
+ <input value="1" type="checkbox" id="simple_history_enable_rss_feed" name="simple_history_enable_rss_feed" <?php checked($this->isRssEnabled(), 1); ?> />
146
+ <label for="simple_history_enable_rss_feed"><?php _e("Enable RSS feed", 'simple-history') ?></label>
147
+ <?php
148
+ }
149
+
150
+ /**
151
+ * Sanitize RSS enabled/disabled status on update settings
152
+ */
153
+ public function updateRssStatus($field)
154
+ {
155
+
156
+ if ($field === "1") {
157
+ return "1";
158
+ }
159
+
160
+ return "0";
161
+ }
162
+
163
+
164
+ /**
165
+ * Check if current request is a request for the RSS feed
166
+ */
167
+ public function checkForRssFeedRequest()
168
+ {
169
+ // check for RSS
170
+ // don't know if this is the right way to do this, but it seems to work!
171
+ if (isset($_GET["simple_history_get_rss"])) {
172
+ $this->outputRss();
173
+ exit;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Modify capability check so all users reading rss feed (logged in or not) can read all loggers
179
+ */
180
+ public function onCanReadSingleLogger($user_can_read_logger, $logger_instance, $user_id)
181
+ {
182
+ $user_can_read_logger = true;
183
+
184
+ return $user_can_read_logger;
185
+ }
186
+
187
+ /**
188
+ * Output RSS
189
+ */
190
+ public function outputRss()
191
+ {
192
+
193
+ $rss_secret_option = get_option("simple_history_rss_secret");
194
+ $rss_secret_get = isset($_GET["rss_secret"]) ? $_GET["rss_secret"] : "";
195
+
196
+ if (empty($rss_secret_option) || empty($rss_secret_get)) {
197
+ die();
198
+ }
199
+
200
+ $rss_show = true;
201
+ $rss_show = apply_filters("simple_history/rss_feed_show", $rss_show);
202
+ if (! $rss_show || ! $this->isRssEnabled()) {
203
+ wp_die('Nothing here.');
204
+ }
205
+
206
+ header("Content-Type: text/xml; charset=utf-8");
207
+ echo '<?xml version="1.0" encoding="UTF-8"?>';
208
+ $self_link = $this->getRssAddress();
209
+
210
+ if ($rss_secret_option === $rss_secret_get) {
211
+ ?>
212
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
213
+ <channel>
214
+ <title><![CDATA[<?php printf(__("History for %s", 'simple-history'), get_bloginfo("name")) ?>]]></title>
215
+ <description><![CDATA[<?php printf(__("WordPress History for %s", 'simple-history'), get_bloginfo("name")) ?>]]></description>
216
+ <link><?php echo get_bloginfo("url") ?></link>
217
+ <atom:link href="<?php echo $self_link; ?>" rel="self" type="application/atom+xml" />
218
+ <?php
219
+
220
+ // Override capability check: if you have a valid rss_secret_key you can read it all
221
+ $action_tag = "simple_history/loggers_user_can_read/can_read_single_logger";
222
+ add_action($action_tag, array($this, "onCanReadSingleLogger"), 10, 3);
223
+
224
+ // Modify header time output so it does not show relative date or time ago-format
225
+ // Because we don't know when a user reads the RSS feed, time ago format may be very inaccurate
226
+ add_action("simple_history/header_just_now_max_time", "__return_zero");
227
+ add_action("simple_history/header_time_ago_max_time", "__return_zero");
228
+
229
+ // Get log rows
230
+ $args = array(
231
+ "posts_per_page" => 10
232
+ );
233
+
234
+ $args = apply_filters("simple_history/rss_feed_args", $args);
235
+
236
+ $logQuery = new SimpleHistoryLogQuery();
237
+ $queryResults = $logQuery->query($args);
238
+
239
+ // Remove capability override after query is done
240
+ // remove_action( $action_tag, array($this, "onCanReadSingleLogger") );
241
+
242
+ foreach ($queryResults["log_rows"] as $row) {
243
+ $header_output = $this->sh->getLogRowHeaderOutput($row);
244
+ $text_output = $this->sh->getLogRowPlainTextOutput($row);
245
+ $details_output = $this->sh->getLogRowDetailsOutput($row);
246
+
247
+ // http://cyber.law.harvard.edu/rss/rss.html#ltguidgtSubelementOfLtitemgt
248
+ //$item_guid = home_url() . "?SimpleHistoryGuid=" . $row->id;
249
+ $item_guid = esc_url(add_query_arg("SimpleHistoryGuid", $row->id, home_url()));
250
+ $item_link = esc_url(add_query_arg("SimpleHistoryGuid", $row->id, home_url()));
251
+
252
+ /**
253
+ * Filter the guid/link URL used in RSS feed.
254
+ * Link will be esc_url'ed by simple history, so no need to do that in your filter
255
+ *
256
+ * @since 2.0.23
257
+ *
258
+ * @param string $item_guid link.
259
+ * @param array $row
260
+ */
261
+ $item_link = apply_filters("simple_history/rss_item_link", $item_link, $row);
262
+ $item_link = esc_url($item_link);
263
+
264
+ $item_title = sprintf(
265
+ '%2$s',
266
+ $this->sh->getLogLevelTranslated($row->level),
267
+ wp_kses($text_output, array())
268
+ );
269
+
270
+ $level_output = sprintf(__('Severity level: %1$s'), $this->sh->getLogLevelTranslated($row->level));
271
+
272
+ ?>
273
+ <item>
274
+ <title><![CDATA[<?php echo $item_title; ?>]]></title>
275
+ <description><![CDATA[
276
+ <p><?php echo $header_output ?></p>
277
+ <p><?php echo $text_output ?></p>
278
+ <div><?php echo $details_output ?></div>
279
+ <p><?php echo $level_output ?></p>
280
+ <?php
281
+ $occasions = $row->subsequentOccasions - 1;
282
+ if ($occasions) {
283
+ printf(
284
+ _n('+%1$s occasion', '+%1$s occasions', $occasions, 'simple-history'),
285
+ $occasions
286
+ );
287
+ }
288
+ ?>
289
+ ]]></description>
290
+ <?php
291
+ // author must be email to validate, but the field is optional, so we skip it
292
+ /* <author><?php echo $row->initiator ?></author> */
293
+ ?>
294
+ <pubDate><?php echo date("D, d M Y H:i:s", strtotime($row->date)) ?> GMT</pubDate>
295
+ <guid isPermaLink="false"><![CDATA[<?php echo $item_guid ?>]]></guid>
296
+ <link><![CDATA[<?php echo $item_link ?>]]></link>
297
+ </item>
298
+ <?php
299
+ /*
300
+ [0] =&gt; stdClass Object
301
+ (
302
+ [id] =&gt; 27324
303
+ [logger] =&gt; SimplePluginLogger
304
+ [level] =&gt; info
305
+ [date] =&gt; 2014-10-15 06:50:01
306
+ [message] =&gt; Updated plugin &quot;{plugin_name}&quot; from {plugin_prev_version} to {plugin_version}
307
+ [type] =&gt;
308
+ [initiator] =&gt; wp_user
309
+ [occasionsID] =&gt; 75e8aeab3e43b37f8a458f3744c4995f
310
+ [subsequentOccasions] =&gt; 1
311
+ [rep] =&gt; 1
312
+ [repeated] =&gt; 1
313
+ [occasionsIDType] =&gt; 75e8aeab3e43b37f8a458f3744c4995f
314
+ [context] =&gt; Array
315
+ (
316
+ [plugin_slug] =&gt; google-analytics-for-wordpress
317
+ [plugin_name] =&gt; Google Analytics by Yoast
318
+ [plugin_title] =&gt; &lt;a href=&quot;https://yoast.com/wordpress/plugins/google-analytics/#utm_source=wordpress&amp;#038;utm_medium=plugin&amp;#038;utm_campaign=wpgaplugin&amp;#038;utm_content=v504&quot;&gt;Google Analytics by Yoast&lt;/a&gt;
319
+ [plugin_description] =&gt; This plugin makes it simple to add Google Analytics to your WordPress blog, adding lots of features, eg. error page, search result and automatic clickout and download tracking. &lt;cite&gt;By &lt;a href=&quot;https://yoast.com/&quot;&gt;Team Yoast&lt;/a&gt;.&lt;/cite&gt;
320
+ [plugin_author] =&gt; &lt;a href=&quot;https://yoast.com/&quot;&gt;Team Yoast&lt;/a&gt;
321
+ [plugin_version] =&gt; 5.0.7
322
+ [plugin_url] =&gt; https://yoast.com/wordpress/plugins/google-analytics/#utm_source=wordpress&amp;#038;utm_medium=plugin&amp;#038;utm_campaign=wpgaplugin&amp;#038;utm_content=v504
323
+ [plugin_update_info_plugin] =&gt; google-analytics-for-wordpress/googleanalytics.php
324
+ [plugin_update_info_package] =&gt; https://downloads.wordpress.org/plugin/google-analytics-for-wordpress.5.0.7.zip
325
+ [plugin_prev_version] =&gt; 5.0.6
326
+ [_message_key] =&gt; plugin_bulk_updated
327
+ [_user_id] =&gt; 1
328
+ [_user_login] =&gt; admin
329
+ [_user_email] =&gt; par.thernstrom@gmail.com
330
+ [_server_remote_addr] =&gt; ::1
331
+ [_server_http_referer] =&gt; http://playground-root.ep/wp-admin/update-core.php?action=do-plugin-upgrade
332
+ )
333
+
334
+ )
335
+ */
336
+ } // foreach
337
+
338
+ ?>
339
+ </channel>
340
+ </rss>
341
+ <?php
342
+ } else {
343
+ // RSS secret was not ok
344
+ ?>
345
+ <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
346
+ <channel>
347
+ <title><?php printf(__("History for %s", 'simple-history'), get_bloginfo("name")) ?></title>
348
+ <description><?php printf(__("WordPress History for %s", 'simple-history'), get_bloginfo("name")) ?></description>
349
+ <link><?php echo home_url() ?></link>
350
+ <item>
351
+ <title><?php _e("Wrong RSS secret", 'simple-history')?></title>
352
+ <description><?php _e("Your RSS secret for Simple History RSS feed is wrong. Please see WordPress settings for current link to the RSS feed.", 'simple-history')?></description>
353
+ <pubDate><?php echo date("D, d M Y H:i:s", time()) ?> GMT</pubDate>
354
+ <guid><?php echo home_url() . "?SimpleHistoryGuid=wrong-secret" ?></guid>
355
+ </item>
356
+ </channel>
357
+ </rss>
358
+ <?php
359
+ }
360
+ } // rss
361
+
362
+ /**
363
+ * Create a new RSS secret
364
+ *
365
+ * @return string new secret
366
+ */
367
+ public function updateRssSecret()
368
+ {
369
+
370
+ $rss_secret = "";
371
+
372
+ for ($i=0; $i<20; $i++) {
373
+ $rss_secret .= chr(rand(97, 122));
374
+ }
375
+
376
+ update_option("simple_history_rss_secret", $rss_secret);
377
+
378
+ return $rss_secret;
379
+ }
380
+
381
+ /**
382
+ * Output for settings field that show current RSS address
383
+ */
384
+ public function settingsFieldRss()
385
+ {
386
+
387
+ $rss_address = $this->getRssAddress();
388
+
389
+ echo "<p><code><a href='$rss_address'>$rss_address</a></code></p>";
390
+ }
391
+
392
+ /**
393
+ * Output for settings field that regenerates the RSS adress/secret
394
+ */
395
+ public function settingsFieldRssRegenerate()
396
+ {
397
+
398
+ $update_link = esc_url(add_query_arg("", ""));
399
+ $update_link = wp_nonce_url($update_link, "simple_history_rss_update_secret", "simple_history_rss_secret_regenerate_nonce");
400
+
401
+ echo "<p>";
402
+ _e("You can generate a new address for the RSS feed. This is useful if you think that the address has fallen into the wrong hands.", 'simple-history');
403
+ echo "</p>";
404
+
405
+ echo "<p>";
406
+ printf(
407
+ '<a class="button" href="%1$s">%2$s</a>',
408
+ $update_link, // 1
409
+ __('Generate new address', "simple-history") // 2
410
+ );
411
+
412
+ echo "</p>";
413
+ }
414
+
415
+ /**
416
+ * Get the URL to the RSS feed
417
+ *
418
+ * @return string URL
419
+ */
420
+ public function getRssAddress()
421
+ {
422
+
423
+ $rss_secret = get_option("simple_history_rss_secret");
424
+ $rss_address = add_query_arg(
425
+ array("simple_history_get_rss" => "1", "rss_secret" => $rss_secret),
426
+ get_bloginfo("url") . "/"
427
+ );
428
+ $rss_address = esc_url($rss_address);
429
+ // $rss_address = htmlspecialchars($rss_address, ENT_COMPAT, "UTF-8");
430
+
431
+ return $rss_address;
432
+ }
433
+
434
+ /**
435
+ * Content for section intro. Leave it be, even if empty.
436
+ * Called from add_sections_setting.
437
+ */
438
+ public function settingsSectionOutput()
439
+ {
440
+
441
+ echo "<p>";
442
+ _e("Simple History has a RSS feed which you can subscribe to and receive log updates. Make sure you only share the feed with people you trust, since it can contain sensitive or confidential information.", 'simple-history');
443
+ echo "</p>";
444
+ }
445
  } // end rss class
examples/examples.php CHANGED
@@ -16,6 +16,23 @@ define("SIMPLE_HISTORY_LOG_DEBUG", true);
16
  * Some examples of filter usage and so on
17
  */
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  // Do not log some post types, for example pages and attachments in this case
20
  add_filter( "simple_history/log/do_log", function( $do_log = null, $level = null, $message = null, $context = null, $logger = null) {
21
 
16
  * Some examples of filter usage and so on
17
  */
18
 
19
+ // Modify who can read a logger
20
+ // Modify the if part to give users access or no access to a logger
21
+ add_filter( 'simple_history/loggers_user_can_read/can_read_single_logger', function( $user_can_read_logger, $logger_instance, $user_id ) {
22
+
23
+ // in this example user with id 3 gets access to the post logger
24
+ // while user with id 8 does not get any access to it
25
+ if ( $logger_instance->slug == "SimplePostLogger" && $user_id === 3 ) {
26
+ $user_can_read_logger = true;
27
+ } else if ( $logger_instance->slug == "SimplePostLogger" && $user_id === 9 ) {
28
+ $user_can_read_logger = false;
29
+ }
30
+
31
+ return $user_can_read_logger;
32
+
33
+ }, 10, 3 );
34
+
35
+
36
  // Do not log some post types, for example pages and attachments in this case
37
  add_filter( "simple_history/log/do_log", function( $do_log = null, $level = null, $message = null, $context = null, $logger = null) {
38
 
inc/SimpleHistory.php CHANGED
@@ -887,7 +887,8 @@ class SimpleHistory {
887
  $loggersDir . "PluginEnableMediaReplaceLogger.php",
888
  $loggersDir . "Plugin_UltimateMembers_Logger.php",
889
  $loggersDir . "Plugin_LimitLoginAttempts.php",
890
- $loggersDir . "Plugin_Redirection.php",
 
891
  );
892
 
893
  // SimpleLogger.php must be loaded first and always since the other loggers extend it
887
  $loggersDir . "PluginEnableMediaReplaceLogger.php",
888
  $loggersDir . "Plugin_UltimateMembers_Logger.php",
889
  $loggersDir . "Plugin_LimitLoginAttempts.php",
890
+ $loggersDir . "Plugin_Redirection.php",
891
+ $loggersDir . "Plugin_DuplicatePost.php",
892
  );
893
 
894
  // SimpleLogger.php must be loaded first and always since the other loggers extend it
index.php CHANGED
@@ -5,7 +5,7 @@ Plugin URI: http://simple-history.com
5
  Text Domain: simple-history
6
  Domain Path: /languages
7
  Description: Plugin that logs various things that occur in WordPress and then presents those events in a very nice GUI.
8
- Version: 2.13
9
  Author: Pär Thernström
10
  Author URI: http://simple-history.com/
11
  License: GPL2
@@ -42,7 +42,7 @@ if ( version_compare( phpversion(), "5.3", ">=") ) {
42
  // register_activation_hook( trailingslashit(WP_PLUGIN_DIR) . trailingslashit( plugin_basename(__DIR__) ) . "index.php" , array("SimpleHistory", "on_plugin_activate" ) );
43
 
44
  if ( ! defined( 'SIMPLE_HISTORY_VERSION' ) ) {
45
- define( 'SIMPLE_HISTORY_VERSION', '2.13' );
46
  }
47
 
48
  if ( ! defined( 'SIMPLE_HISTORY_PATH' ) ) {
5
  Text Domain: simple-history
6
  Domain Path: /languages
7
  Description: Plugin that logs various things that occur in WordPress and then presents those events in a very nice GUI.
8
+ Version: 2.14
9
  Author: Pär Thernström
10
  Author URI: http://simple-history.com/
11
  License: GPL2
42
  // register_activation_hook( trailingslashit(WP_PLUGIN_DIR) . trailingslashit( plugin_basename(__DIR__) ) . "index.php" , array("SimpleHistory", "on_plugin_activate" ) );
43
 
44
  if ( ! defined( 'SIMPLE_HISTORY_VERSION' ) ) {
45
+ define( 'SIMPLE_HISTORY_VERSION', '2.14' );
46
  }
47
 
48
  if ( ! defined( 'SIMPLE_HISTORY_PATH' ) ) {
loggers/Plugin_DuplicatePost.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ defined('ABSPATH') or die();
4
+
5
+ /**
6
+ * Logger for the Duplicate Post plugin
7
+ * Post Duplicator (https://sv.wordpress.org/plugins/duplicate-post/)
8
+ *
9
+ * @package SimpleHistory
10
+ * @since 2.13
11
+ */
12
+ if (! class_exists("Plugin_DuplicatePost")) {
13
+
14
+ class Plugin_DuplicatePost extends SimpleLogger
15
+ {
16
+ public $slug = __CLASS__;
17
+
18
+ public function getInfo()
19
+ {
20
+ $arr_info = array(
21
+ "name" => "Plugin Duplicate Posts",
22
+ "description" => _x("Logs posts and pages cloned using plugin Duplicate Post", "Logger: Plugin Duplicate Post", "simple-history"),
23
+ "name_via" => _x("Using plugin Duplicate Posts", "Logger: Plugin Duplicate Post", "simple-history"),
24
+ "capability" => "manage_options",
25
+ "messages" => array(
26
+ 'post_duplicated' => _x('Cloned "{duplicated_post_title}" to a new post', "Logger: Plugin Duplicate Post", 'simple-history')
27
+ ),
28
+ );
29
+
30
+ return $arr_info;
31
+ }
32
+
33
+ public function loaded()
34
+ {
35
+ require_once(ABSPATH . 'wp-admin/includes/plugin.php');
36
+
37
+ $pluginFilePath = 'duplicate-post/duplicate-post.php';
38
+ $isPluginActive = is_plugin_active('duplicate-post/duplicate-post.php');
39
+
40
+ if (!$isPluginActive) {
41
+ return;
42
+ }
43
+
44
+ // When a copy have been made of a post or page
45
+ // the action 'dp_duplicate_page' or 'dp_duplicate_post'
46
+ // is fired with args $new_post_id, $post, $status.
47
+ // We add actions with prio 20 so we probably run after
48
+ // the plugins own
49
+ add_action('dp_duplicate_post', array($this, 'onDpDuplicatePost'), 100, 3);
50
+ add_action('dp_duplicate_page', array($this, 'onDpDuplicatePost'), 100, 3);
51
+ }
52
+
53
+ /**
54
+ * A post or page was duplicated
55
+ *
56
+ * @param $new_post_id
57
+ * @param $post old post that a copy was made of
58
+ * @param $status
59
+ */
60
+ public function onDpDuplicatePost($newPostID, $post, $status)
61
+ {
62
+ $new_post = get_post($newPostID);
63
+
64
+ $context = [
65
+ "new_post_title" => $new_post->post_title,
66
+ "new_post_id" => $new_post->ID,
67
+ "duplicated_post_title" => $post->post_title,
68
+ "duplicated_post_id" => $post->ID,
69
+ // "duplicate_new_post_id" => $newPostID,
70
+ // "status" => $status
71
+ ];
72
+
73
+ $this->infoMessage(
74
+ "post_duplicated",
75
+ $context
76
+ );
77
+ }
78
+
79
+ /**
80
+ * Modify plain output to include link to post
81
+ */
82
+ public function getLogRowPlainTextOutput($row) {
83
+
84
+ $context = $row->context;
85
+ $new_post_id = isset($context["new_post_id"]) ? $context["new_post_id"] : null;
86
+ $duplicated_post_id = isset($context["duplicated_post_id"]) ? $context["duplicated_post_id"] : null;
87
+ $duplicated_post_title = isset($context["duplicated_post_title"]) ? $context["duplicated_post_title"] : null;
88
+ $message_key = isset($context["_message_key"]) ? $context["_message_key"] : null;
89
+
90
+ $message = $row->message;
91
+
92
+ // Check if post still is available
93
+ // It will return a WP_Post Object if post still is in system
94
+ // If post is deleted from trash (not just moved there), then null is returned
95
+ $postDuplicated = get_post($duplicated_post_id);
96
+ $post_is_available = is_a($postDuplicated, "WP_Post");
97
+
98
+ // Try to get singular name
99
+ $post_type = isset($postDuplicated->post_type) ? $postDuplicated->post_type : "";
100
+ $post_type_obj = get_post_type_object($post_type);
101
+
102
+ if (!is_null($post_type_obj)) {
103
+ if (!empty ($post_type_obj->labels->singular_name) ) {
104
+ $context["duplicated_post_post_type_singular_name"] = strtolower($post_type_obj->labels->singular_name);
105
+ }
106
+ }
107
+
108
+ $context["duplicated_post_edit_link"] = get_edit_post_link($duplicated_post_id);
109
+ $context["new_post_edit_link"] = get_edit_post_link($new_post_id);
110
+
111
+ // If post is not available any longer then we can't link to it, so keep plain message then
112
+ // Also keep plain format if user is not allowed to edit post (edit link is empty)
113
+ if ($post_is_available && $context["duplicated_post_edit_link"]) {
114
+ $message = _x('Cloned {duplicated_post_post_type_singular_name} <a href="{duplicated_post_edit_link}">"{duplicated_post_title}"</a> to <a href="{new_post_edit_link}">a new {duplicated_post_post_type_singular_name}</a>', "Logger: Plugin Duplicate Post", "simple-history");
115
+ } // post still available
116
+
117
+ $context["new_post_edit_link"] = isset($context["new_post_edit_link"]) ? esc_html($context["new_post_edit_link"]) : "";
118
+
119
+ $context["duplicated_post_edit_link"] = isset($context["duplicated_post_edit_link"]) ? esc_html($context["duplicated_post_edit_link"]) : "";
120
+
121
+ $context["duplicated_post_title"] = isset($context["duplicated_post_title"]) ? esc_html($context["duplicated_post_title"]) : "";
122
+
123
+ $context["duplicated_post_title"] = isset($context["duplicated_post_title"]) ? esc_html($context["duplicated_post_title"]) : "";
124
+
125
+ $context["duplicated_post_post_type_singular_name"] = isset($context["duplicated_post_post_type_singular_name"]) ? esc_html($context["duplicated_post_post_type_singular_name"]) : "";
126
+
127
+ return $this->interpolate($message, $context, $row);
128
+ }
129
+ } // class
130
+ } // class exists
loggers/Plugin_LimitLoginAttempts.php CHANGED
@@ -46,6 +46,18 @@ if ( ! class_exists("Plugin_LimitLoginAttempts") ) {
46
 
47
  function loaded() {
48
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  add_filter( "pre_option_limit_login_lockouts_total", array( $this, "on_option_limit_login_lockouts_total" ), 10, 1 );
50
 
51
  add_action( "load-settings_page_limit-login-attempts", array( $this, "on_load_settings_page" ), 10, 1 );
46
 
47
  function loaded() {
48
 
49
+ require_once(ABSPATH . 'wp-admin/includes/plugin.php');
50
+
51
+ $pluginFilePath = 'limit-login-attempts/limit-login-attempts.php';
52
+ $isPluginActive = is_plugin_active($pluginFilePath);
53
+
54
+ // Only continue to add filters if plugin is active.
55
+ // This minimise the risk of plugin errors, because plugin
56
+ // has been forked to new versions.
57
+ if (!$isPluginActive) {
58
+ return;
59
+ }
60
+
61
  add_filter( "pre_option_limit_login_lockouts_total", array( $this, "on_option_limit_login_lockouts_total" ), 10, 1 );
62
 
63
  add_action( "load-settings_page_limit-login-attempts", array( $this, "on_load_settings_page" ), 10, 1 );
loggers/SimplePostLogger.php CHANGED
@@ -302,7 +302,7 @@ class SimplePostLogger extends SimpleLogger
302
 
303
  /**
304
  * Fired when a post has changed status
305
- * Only run in certain cases,
306
  * because when always enabled it catches a lots of edits made by plugins during cron jobs etc,
307
  * which by definition is not wrong, but perhaps not wanted/annoying
308
  */
@@ -660,7 +660,7 @@ class SimplePostLogger extends SimpleLogger
660
  }
661
 
662
  /**
663
- * Modify plain output to inlcude link to post
664
  */
665
  public function getLogRowPlainTextOutput($row) {
666
 
@@ -671,7 +671,7 @@ class SimplePostLogger extends SimpleLogger
671
  $message = $row->message;
672
 
673
  // Check if post still is available
674
- // It wil return a WP_Post Object if post still is in system
675
  // If post is deleted from trash (not just moved there), then null is returned
676
  $post = get_post( $post_id );
677
  $post_is_available = is_a($post, "WP_Post");
302
 
303
  /**
304
  * Fired when a post has changed status
305
+ * Only run in certain cases,
306
  * because when always enabled it catches a lots of edits made by plugins during cron jobs etc,
307
  * which by definition is not wrong, but perhaps not wanted/annoying
308
  */
660
  }
661
 
662
  /**
663
+ * Modify plain output to include link to post
664
  */
665
  public function getLogRowPlainTextOutput($row) {
666
 
671
  $message = $row->message;
672
 
673
  // Check if post still is available
674
+ // It will return a WP_Post Object if post still is in system
675
  // If post is deleted from trash (not just moved there), then null is returned
676
  $post = get_post( $post_id );
677
  $post_is_available = is_a($post, "WP_Post");
loggers/SimpleUserLogger.php CHANGED
@@ -1,1013 +1,966 @@
1
  <?php
2
 
3
- defined( 'ABSPATH' ) or die();
4
-
5
  /**
6
  * Logs changes to user logins (and logouts)
7
  */
8
- class SimpleUserLogger extends SimpleLogger {
9
-
10
- public $slug = __CLASS__;
11
-
12
- /**
13
- * Get array with information about this logger
14
- *
15
- * @return array
16
- */
17
- function getInfo() {
18
-
19
- $arr_info = array(
20
- "name" => __("User Logger", "simple-history"),
21
- "description" => __("Logs user logins, logouts, and failed logins", "simple-history"),
22
- "capability" => "edit_users",
23
- "messages" => array(
24
- 'user_login_failed' => __('Failed to login with username "{login}" (incorrect password entered)', "simple-history"),
25
- 'user_unknown_login_failed' => __('Failed to login with username "{failed_username}" (username does not exist)', "simple-history"),
26
- 'user_logged_in' => __('Logged in', "simple-history"),
27
- 'user_unknown_logged_in' => __("Unknown user logged in", "simple-history"),
28
- 'user_logged_out' => __("Logged out", "simple-history"),
29
- 'user_updated_profile' => __("Edited the profile for user {edited_user_login} ({edited_user_email})", "simple-history"),
30
- 'user_created' => __("Created user {created_user_login} ({created_user_email}) with role {created_user_role}", "simple-history"),
31
- 'user_deleted' => __("Deleted user {deleted_user_login} ({deleted_user_email})", "simple-history"),
32
- "user_password_reseted" => __("Reset their password", "simple-history"),
33
- "user_requested_password_reset_link" => __("Requested a password reset link for user with login '{user_login}' and email '{user_email}'", "simple-history"),
34
-
35
- /*
36
- Text used in admin:
37
- Log Out of All Other Sessions
38
- Left your account logged in at a public computer? Lost your phone? This will log you out everywhere except your current browser
39
- */
40
- 'user_session_destroy_others' => _x(
41
- 'Logged out from all other sessions',
42
- 'User destroys other login sessions for themself',
43
- 'simple-history'
44
- ),
45
- /*
46
- Text used in admin:
47
- 'Log %s out of all sessions' ), $profileuser->display_name );
48
- */
49
- 'user_session_destroy_everywhere' => _x(
50
- 'Logged out "{user_display_name}" from all sessions',
51
- 'User destroys all login sessions for a user',
52
- 'simple-history'
53
- ),
54
- ),
55
-
56
- "labels" => array(
57
- "search" => array(
58
- "label" => _x("Users", "User logger: search", "simple-history"),
59
- "label_all" => _x("All user activity", "User logger: search", "simple-history"),
60
- "options" => array(
61
- _x("Successful user logins", "User logger: search", "simple-history") => array(
62
- "user_logged_in",
63
- "user_unknown_logged_in",
64
- ),
65
- _x("Failed user logins", "User logger: search", "simple-history") => array(
66
- 'user_login_failed',
67
- 'user_unknown_login_failed',
68
- ),
69
- _x('User logouts', 'User logger: search', 'simple-history') => array(
70
- "user_logged_out",
71
- ),
72
- _x('Created users', 'User logger: search', 'simple-history') => array(
73
- "user_created",
74
- ),
75
- _x("User profile updates", "User logger: search", "simple-history") => array(
76
- "user_updated_profile",
77
- ),
78
- _x('User deletions', 'User logger: search', 'simple-history') => array(
79
- "user_deleted",
80
- ),
81
-
82
- ),
83
- ), // end search
84
-
85
- ), // end labels
86
-
87
- );
88
- #sf_d($arr_info);exit;
89
- return $arr_info;
90
-
91
- }
92
-
93
- /**
94
- * Add actions and filters when logger is loaded by Simple History
95
- */
96
- public function loaded() {
97
-
98
- // Plain logins and logouts
99
- add_action("wp_login", array($this, "on_wp_login"), 10, 3);
100
- add_action("wp_logout", array($this, "on_wp_logout"));
101
-
102
- // Failed login attempt to username that exists
103
- add_action("wp_authenticate_user", array($this, "on_wp_authenticate_user"), 10, 2);
104
-
105
- // Failed to login to user that did not exist (perhaps brute force)
106
- // run this later than 10 because wordpress own email login check is done with prio 20
107
- // so if we run at 10 we just get null
108
- add_filter('authenticate', array($this, "on_authenticate"), 30, 3);
109
-
110
- // User is changed
111
- #add_action("profile_update", array($this, "on_profile_update"), 10, 2);
112
-
113
- // User is created
114
- add_action("user_register", array($this, "on_user_register"), 10, 2);
115
-
116
- // User is deleted
117
- add_action('delete_user', array($this, "on_delete_user"), 10, 2);
118
-
119
- // User sessions is destroyed. AJAX call that we hook onto early.
120
- add_action("wp_ajax_destroy-sessions", array($this, "on_destroy_user_session"), 0);
121
-
122
- // User reaches reset password (from link or only from user created link)
123
- add_action( 'validate_password_reset', array( $this, "on_validate_password_reset" ), 10, 2 );
124
-
125
- add_action( 'retrieve_password_message', array( $this, "on_retrieve_password_message" ), 10, 4 );
126
-
127
- add_filter( 'insert_user_meta', array( $this, "on_insert_user_meta" ), 10, 3 );
128
-
129
-
130
- }
131
-
132
- /*
133
- * Called before the user is updated
134
- *
135
- * Filter a user's meta values and keys before the user is created or updated.
136
- *
137
- * Does not include contact methods. These are added using `wp_get_user_contact_methods( $user )`.
138
- *
139
- * @param array $meta {
140
- * Default meta values and keys for the user.
141
- *
142
- * @type string $nickname The user's nickname. Default is the user's username.
143
- * @type string $first_name The user's first name.
144
- * @type string $last_name The user's last name.
145
- * @type string $description The user's description.
146
- * @type bool $rich_editing Whether to enable the rich-editor for the user. False if not empty.
147
- * @type bool $comment_shortcuts Whether to enable keyboard shortcuts for the user. Default false.
148
- * @type string $admin_color The color scheme for a user's admin screen. Default 'fresh'.
149
- * @type int|bool $use_ssl Whether to force SSL on the user's admin area. 0|false if SSL is
150
- * not forced.
151
- * @type bool $show_admin_bar_front Whether to show the admin bar on the front end for the user.
152
- * Default true.
153
- * }
154
- * @param WP_User $user User object.
155
- * @param bool $update Whether the user is being updated rather than created.
156
- */
157
- function on_insert_user_meta( $meta, $user, $update ) {
158
-
159
- // We only log updates here
160
- if ( ! $update ) {
161
- return $meta;
162
- }
163
-
164
- // $user should be set, but check just in case
165
- if ( empty( $user ) || ! is_object( $user ) ) {
166
- return $meta;
167
- }
168
-
169
- // Make of copy of the posted data, because we change the keys
170
- $posted_data = $_POST;
171
- $posted_data = stripslashes_deep( $posted_data );
172
-
173
- // Paranoid mode, just in case some other plugin fires the "insert_user_meta" filter and the user.php file is not loaded for some super wierd reason
174
- if ( ! function_exists( "_get_additional_user_keys" ) ) {
175
- return $meta;
176
- }
177
-
178
- // Get the default fields to include. This includes contact methods (including filter, so more could have been added)
179
- $arr_keys_to_check = _get_additional_user_keys( $user );
180
-
181
- // Somehow some fields are not include above, so add them manually
182
- $arr_keys_to_check = array_merge( $arr_keys_to_check, array("user_email", "user_url", "display_name") );
183
-
184
- // Skip some keys, because to much info or I don't know what they are
185
- $arr_keys_to_check = array_diff( $arr_keys_to_check, array("use_ssl") );
186
-
187
- // Some keys have different ways of getting data from user
188
- // so change posted object to match those
189
- $posted_data["user_url"] = isset( $posted_data["url"] ) ? $posted_data["url"] : null;
190
- $posted_data["show_admin_bar_front"] = isset( $posted_data["admin_bar_front"] ) ? true : null;
191
- $posted_data["user_email"] = isset( $posted_data["email"] ) ? $posted_data["email"] : null;
192
-
193
- // Display name publicly as = POST "display_name"
194
- #var_dump($user->display_name);
195
-
196
- // Set vals for Enable keyboard shortcuts for comment moderation
197
- $posted_data['comment_shortcuts'] = isset( $posted_data['comment_shortcuts'] ) ? "true" : "false";
198
-
199
- // Set vals for Disable the visual editor when writing
200
- // posted val = string "false" = yes, disable
201
- $posted_data['rich_editing'] = isset( $posted_data['rich_editing'] ) ? "false" : "true";
202
-
203
- // Set vals for Show Toolbar when viewing site
204
- $posted_data['show_admin_bar_front'] = isset( $posted_data['admin_bar_front'] ) ? "true" : "false";
205
-
206
- // if checkbox is checked in admin then this is the saved value on the user object
207
- // @todo:
208
-
209
- // Check if password was updated
210
- $password_changed = false;
211
- if ( ! empty( $posted_data['pass1'] ) && ! empty( $posted_data['pass2'] ) && $posted_data['pass1'] == $posted_data['pass2'] ) {
212
- $password_changed = 1;
213
- }
214
-
215
- // Check if role was changed
216
- //[role] => bbp_moderator
217
- $role_changed = false;
218
-
219
- // if user is network admin then role dropdown does not exist and role is not posted here
220
- $new_role = isset( $posted_data["role"] ) ? $posted_data["role"] : null;
221
-
222
- if ( $new_role ) {
223
- // as done in user-edit.php
224
- // Compare user role against currently editable roles
225
- $user_roles = array_intersect( array_values( $user->roles ), array_keys( get_editable_roles() ) );
226
- $old_role = reset( $user_roles );
227
-
228
- $role_changed = $new_role != $old_role;
229
- }
230
-
231
- // Will contain the differences
232
- $user_data_diff = array();
233
-
234
- // Check all keys for diff values
235
- foreach ( $arr_keys_to_check as $one_key_to_check ) {
236
-
237
- $old_val = $user->$one_key_to_check;
238
- $new_val = isset( $posted_data[ $one_key_to_check ] ) ? $posted_data[ $one_key_to_check ] : null;
239
-
240
- #echo "<hr>key: $one_key_to_check";
241
- #echo "<br>old val: $old_val";
242
- #echo "<br>new val: $new_val";
243
-
244
- // new val must be set, because otherwise we are not setting anything
245
- if ( ! isset( $new_val ) ) {
246
- continue;
247
- }
248
-
249
- $user_data_diff = $this->add_diff($user_data_diff, $one_key_to_check, $old_val, $new_val);
250
-
251
- }
252
-
253
- // Setup basic context
254
- $context = array(
255
- "edited_user_id" => $user->ID,
256
- "edited_user_email" => $user->user_email,
257
- "edited_user_login" => $user->user_login,
258
- "server_http_user_agent" => isset( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null,
259
- );
260
-
261
- if ( $password_changed ) {
262
- $context["edited_user_password_changed"] = "1";
263
- }
264
-
265
- if ( $role_changed ) {
266
- $context["user_prev_role"] = $old_role;
267
- $context["user_new_role"] = $new_role;
268
- }
269
-
270
- // Add diff to context
271
- if ( $user_data_diff ) {
272
-
273
- foreach ( $user_data_diff as $one_diff_key => $one_diff_vals ) {
274
- /*
275
- One diff looks like:
276
- "nickname": {
277
- "old": "MyOldNick",
278
- "new": "MyNewNick"
279
- }
280
- */
281
- $context["user_prev_{$one_diff_key}"] = $one_diff_vals["old"];
282
- $context["user_new_{$one_diff_key}"] = $one_diff_vals["new"];
283
- }
284
-
285
- }
286
-
287
-
288
- $this->infoMessage("user_updated_profile", $context);
289
-
290
- return $meta;
291
-
292
- }
293
-
294
- /**
295
- *
296
- * user requests a reset password link
297
- *
298
- */
299
- function on_retrieve_password_message( $message, $key, $user_login, $user_data ) {
300
-
301
- if ( isset( $_GET["action"] ) && ( "lostpassword" == $_GET["action"] ) ) {
302
-
303
- $context = array(
304
- "_initiator" => SimpleLoggerLogInitiators::WEB_USER,
305
- "message" => $message,
306
- "key" => $key,
307
- "user_login" => $user_login,
308
- );
309
-
310
- if ( is_a( $user_data, "WP_User" ) ) {
311
-
312
- $context["user_email"] = $user_data->user_email;
313
-
314
- }
315
-
316
- $this->noticeMessage( "user_requested_password_reset_link", $context );
317
-
318
- }
319
-
320
- return $message;
321
-
322
- }
323
-
324
- /**
325
- * Fired before the password reset procedure is validated.
326
- *
327
- * @param object $errors WP Error object.
328
- * @param WP_User|WP_Error $user WP_User object if the login and reset key match. WP_Error object otherwise.
329
- */
330
- function on_validate_password_reset( $errors, $user ) {
331
-
332
- /*
333
- User visits the forgot password screen
334
- $errors object are empty
335
- $user contains a user
336
- $_post is empty
337
-
338
- User resets password
339
- $errors empty
340
- $user user object
341
- $_post
342
-
343
- */
344
-
345
- $context = array();
346
-
347
- if ( is_a( $user, "WP_User") ) {
348
-
349
- $context["_initiator"] = SimpleLoggerLogInitiators::WP_USER;
350
- $context["_user_id"] = $user->ID;
351
- $context["_user_login"] = $user->user_login;
352
- $context["_user_email"] = $user->user_email;
353
-
354
- }
355
-
356
- if ( isset($_POST['pass1']) && $_POST['pass1'] != $_POST['pass2'] ) {
357
-
358
- // $errors->add( 'password_reset_mismatch', __( 'The passwords do not match.' ) );
359
- // user failed to reset password
360
-
361
- }
362
-
363
-
364
- if ( ( ! $errors->get_error_code() ) && isset( $_POST['pass1'] ) && !empty( $_POST['pass1'] ) ) {
365
-
366
- // login_header( __( 'Password Reset' ), '<p class="message reset-pass">' . __( 'Your password has been reset.' ) . ' <a href="' . esc_url(
367
- $this->infoMessage( "user_password_reseted", $context );
368
-
369
-
370
- }
371
-
372
-
373
- }
374
-
375
- /**
376
- * Called when user dessions are destroyed from admin
377
- * Can be called for current logged in user = destroy all other sessions
378
- * or for another user = destroy alla sessions for that user
379
- * Fires from AJAX call
380
- *
381
- * @since 2.0.6
382
- */
383
- function on_destroy_user_session() {
384
-
385
- /*
386
- Post params:
387
- nonce: a14df12195
388
- user_id: 1
389
- action: destroy-sessions
390
- */
391
-
392
- $user = get_userdata((int) $_POST['user_id']);
393
-
394
- if ($user) {
395
- if (!current_user_can('edit_user', $user->ID)) {
396
- $user = false;
397
- } elseif (!wp_verify_nonce($_POST['nonce'], 'update-user_' . $user->ID)) {
398
- $user = false;
399
- }
400
- }
401
-
402
- if (!$user) {
403
- // Could not log out user sessions. Please try again.
404
- return;
405
- }
406
-
407
- $sessions = WP_Session_Tokens::get_instance($user->ID);
408
-
409
- $context = array();
410
-
411
- if ($user->ID === get_current_user_id()) {
412
-
413
- $this->infoMessage("user_session_destroy_others");
414
-
415
- } else {
416
-
417
- $context["user_id"] = $user->ID;
418
- $context["user_login"] = $user->user_login;
419
- $context["user_display_name"] = $user->display_name;
420
-
421
- $this->infoMessage("user_session_destroy_everywhere", $context);
422
-
423
- }
424
-
425
- }
426
-
427
- /**
428
- * Fires before a user is deleted from the database.
429
- *
430
- * @param int $user_id ID of the deleted user.
431
- * @param int|null $reassign ID of the user to reassign posts and links to.
432
- * Default null, for no reassignment.
433
- */
434
- public function on_delete_user($user_id, $reassign) {
435
-
436
- $wp_user_to_delete = get_userdata($user_id);
437
-
438
- // wp_user->roles (array) - the roles the user is part of.
439
- $role = null;
440
- if (is_array($wp_user_to_delete->roles) && !empty($wp_user_to_delete->roles[0])) {
441
- $role = $wp_user_to_delete->roles[0];
442
- }
443
-
444
- $context = array(
445
- "deleted_user_id" => $wp_user_to_delete->ID,
446
- "deleted_user_email" => $wp_user_to_delete->user_email,
447
- "deleted_user_login" => $wp_user_to_delete->user_login,
448
- "deleted_user_role" => $role,
449
- "reassign_user_id" => $reassign,
450
- "server_http_user_agent" => isset( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null
451
- );
452
-
453
- // Let's log this as a little bit more significant that just "message"
454
- $this->noticeMessage("user_deleted", $context);
455
-
456
- }
457
-
458
- /**
459
- * Modify plain text row output
460
- * - adds link to user profil
461
- * - change to "your profile" if you're looking at your own edit
462
- */
463
- public function getLogRowPlainTextOutput($row) {
464
-
465
- $context = $row->context;
466
-
467
- $output = parent::getLogRowPlainTextOutput($row);
468
- $current_user_id = get_current_user_id();
469
-
470
- if ( "user_updated_profile" == $context["_message_key"] ) {
471
-
472
- $wp_user = get_user_by( "id", $context["edited_user_id"] );
473
-
474
- // If edited_user_id and _user_id is the same then a user edited their own profile
475
- // Note: it's not the same thing as the currently logged in user (but.. it can be!)
476
- if ( ! empty( $context["_user_id"] ) && $context["edited_user_id"] === $context["_user_id"] ) {
477
-
478
- if ( $wp_user ) {
479
-
480
- $context["edit_profile_link"] = get_edit_user_link( $wp_user->ID );
481
-
482
- $use_you = apply_filters("simple_history/user_logger/plain_text_output_use_you", true);
483
-
484
- //error_log( serialize( $current_user_id) ); // int 1
485
- //error_log( serialize( $context["_user_id"]) ); // string 1
486
-
487
- // User still exist, so link to their profile
488
- if ( (int) $current_user_id === (int) $context["_user_id"] && $use_you ) {
489
-
490
- // User that is viewing the log is the same as the edited user
491
- $msg = __('Edited <a href="{edit_profile_link}">your profile</a>', "simple-history");
492
-
493
- } else {
494
-
495
- $msg = __('Edited <a href="{edit_profile_link}">their profile</a>', "simple-history");
496
-
497
- }
498
-
499
- $output = $this->interpolate($msg, $context, $row);
500
-
501
- } else {
502
-
503
- // User does not exist any longer
504
- $output = __("Edited your profile", "simple-history");
505
-
506
- }
507
-
508
- } else {
509
-
510
- // User edited another users profile
511
- if ( $wp_user ) {
512
-
513
- // Edited user still exist, so link to their profile
514
- $context["edit_profile_link"] = get_edit_user_link($wp_user->ID);
515
- $msg = __('Edited the profile for user <a href="{edit_profile_link}">{edited_user_login} ({edited_user_email})</a>', "simple-history");
516
- $output = $this->interpolate($msg, $context, $row);
517
-
518
- } else {
519
-
520
- // Edited user does not exist any longer
521
-
522
- }
523
-
524
- }
525
-
526
- // if user_updated_profile
527
-
528
- } else if ( "user_created" == $context["_message_key"] ) {
529
-
530
- // A user was created. Create link of username that goes to user profile.
531
- $wp_user = get_user_by( "id", $context["created_user_id"] );
532
-
533
- // If edited_user_id and _user_id is the same then a user edited their own profile
534
- // Note: it's not the same thing as the currently logged in user (but.. it can be!)
535
-
536
- if ( $wp_user ) {
537
-
538
- $context["edit_profile_link"] = get_edit_user_link( $wp_user->ID );
539
-
540
- // User that is viewing the log is the same as the edited user
541
- $msg = __('Created user <a href="{edit_profile_link}">{created_user_login} ({created_user_email})</a> with role {created_user_role}', "simple-history");
542
-
543
- $output = $this->interpolate(
544
- $msg,
545
- $context,
546
- $row
547
- );
548
-
549
- } else {
550
-
551
- // User does not exist any longer, keep original message
552
-
553
-
554
- }
555
-
556
- }
557
-
558
-
559
- return $output;
560
- }
561
-
562
- /**
563
- * User logs in
564
- *
565
- * @param string $user_login
566
- * @param object $user
567
- */
568
- function on_wp_login( $user_login = null, $user = null) {
569
-
570
- $context = array(
571
- "user_login" => $user_login
572
- );
573
-
574
- if ( isset( $user_login ) ) {
575
-
576
- $user_obj = get_user_by( "login", $user_login );
577
-
578
- } else if ( isset( $user ) && isset( $user->ID ) ) {
579
-
580
- $user_obj = get_user_by( "id", $user->ID );
581
-
582
- }
583
-
584
- if ( is_a( $user_obj, "WP_User" ) ) {
585
-
586
- $context = array(
587
- "user_id" => $user_obj->ID,
588
- "user_email" => $user_obj->user_email,
589
- "user_login" => $user_obj->user_login,
590
- );
591
-
592
- // Override some data that is usually set automagically by Simple History
593
- // Because wp_get_current_user() does not return any data yet at this point
594
- $context["_initiator"] = SimpleLoggerLogInitiators::WP_USER;
595
- $context["_user_id"] = $user_obj->ID;
596
- $context["_user_login"] = $user_obj->user_login;
597
- $context["_user_email"] = $user_obj->user_email;
598
- $context["server_http_user_agent"] = isset( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null;
599
-
600
- $this->infoMessage("user_logged_in", $context);
601
-
602
- } else {
603
-
604
- // Could not get any info about the user logging in
605
- $this->warningMessage("user_unknown_logged_in", $context);
606
- }
607
-
608
- }
609
-
610
- /**
611
- * User logs out
612
- * http://codex.wordpress.org/Plugin_API/Action_Reference/wp_logout
613
- */
614
- function on_wp_logout() {
615
-
616
- $this->infoMessage("user_logged_out");
617
-
618
- }
619
-
620
- /**
621
- * User is edited
622
- *
623
- * Called immediately after an existing user is updated.
624
- * @param int $user_id User ID.
625
- * @param object $old_user_data Object containing user's data prior to update.
626
- */
627
- function on_profile_update( $user_id, $old_user_data ) {
628
-
629
- /*
630
- if (!$user_id || !is_numeric($user_id)) {
631
- return;
632
- }
633
-
634
- $wp_user_edited = get_userdata($user_id);
635
-
636
- $context = array(
637
- "edited_user_id" => $wp_user_edited->ID,
638
- "edited_user_email" => $wp_user_edited->user_email,
639
- "edited_user_login" => $wp_user_edited->user_login,
640
- "server_http_user_agent" => isset( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null,
641
- "old_user_data" => $old_user_data
642
- );
643
-
644
-
645
- $this->infoMessage("user_updated_profile", $context);
646
- */
647
-
648
- }
649
-
650
- /**
651
- * User is created
652
- *
653
- * "This action hook allows you to access data for a new user immediately after they are added to the database.
654
- * The user id is passed to hook as an argument."
655
- *
656
- */
657
- function on_user_register( $user_id ) {
658
-
659
- if ( ! $user_id || ! is_numeric( $user_id )) {
660
- return;
661
- }
662
-
663
- $wp_user_added = get_userdata($user_id);
664
-
665
- // wp_user->roles (array) - the roles the user is part of.
666
- $role = null;
667
- if ( is_array( $wp_user_added->roles ) && ! empty( $wp_user_added->roles[0]) ) {
668
- $role = $wp_user_added->roles[0];
669
- }
670
-
671
- $send_user_notification = (int) ( isset( $_POST["send_user_notification"] ) && $_POST["send_user_notification"] );
672
-
673
- $context = array(
674
- "created_user_id" => $wp_user_added->ID,
675
- "created_user_email" => $wp_user_added->user_email,
676
- "created_user_login" => $wp_user_added->user_login, // username
677
- "created_user_role" => $role,
678
- "created_user_first_name" => $wp_user_added->first_name,
679
- "created_user_last_name" => $wp_user_added->last_name,
680
- "created_user_url" => $wp_user_added->user_url,
681
- "send_user_notification" => $send_user_notification,
682
- "server_http_user_agent" => isset( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null
683
- );
684
-
685
- $this->infoMessage("user_created", $context);
686
-
687
- }
688
-
689
- /**
690
- * Log failed login attempt to username that exists
691
- *
692
- * @param object $user user object that was tried to gain access to
693
- * @param string password used
694
- */
695
- function on_wp_authenticate_user($user, $password) {
696
-
697
- // Only log failed attempts
698
- if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
699
-
700
- // Overwrite some vars that Simple History set automagically
701
- $context = array(
702
- "_initiator" => SimpleLoggerLogInitiators::WEB_USER,
703
- "login_id" => $user->ID,
704
- "login_email" => $user->user_email,
705
- "login" => $user->user_login,
706
- "server_http_user_agent" => isset( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null,
707
- "_occasionsID" => __CLASS__ . '/failed_user_login',
708
- );
709
-
710
- /**
711
- * Maybe store password too
712
- * Default is to not do this because of privacy and security
713
- *
714
- * @since 2.0
715
- *
716
- * @param bool $log_password
717
- */
718
- $log_password = false;
719
- $log_password = apply_filters("simple_history/comments_logger/log_failed_password", $log_password);
720
-
721
- if ( $log_password ) {
722
- $context["login_user_password"] = $password;
723
- }
724
-
725
- $this->warningMessage("user_login_failed", $context);
726
-
727
- }
728
-
729
- return $user;
730
-
731
- }
732
-
733
- /**
734
- * Attempt to login to user that does not exist
735
- *
736
- * @param $user (null or WP_User or WP_Error) (required)
737
- * null indicates no process has authenticated the user yet.
738
- * A WP_Error object indicates another process has failed the authentication.
739
- * A WP_User object indicates another process has authenticated the user.
740
- * @param $username The user's username. since 4.5.0 `$username` now accepts an email address.
741
- * @param $password The user's password (encrypted)
742
- */
743
- function on_authenticate( $user, $username, $password ) {
744
-
745
- // Don't log empty usernames
746
- if ( ! trim( $username ) ) {
747
- return $user;
748
- }
749
-
750
- // If null then no auth done yet. Wierd. But what can we do.
751
- if ( is_null( $user ) ) {
752
- return $user;
753
- }
754
-
755
- // If auth ok then $user is a wp_user object
756
- if ( is_a( $user, 'WP_User' ) ) {
757
- return $user;
758
- }
759
-
760
- // If user is a WP_Error object then auth failed
761
- // Error codes can be:
762
- // "incorrect_password" | "empty_password" | "invalid_email" | "invalid_username"
763
- // We only act on invalid emails and invalid usernames
764
- if ( is_a( $user, 'WP_Error' ) && ( $user->get_error_code() == "invalid_username" || $user->get_error_code() == "invalid_email" ) ) {
765
-
766
- $context = array(
767
- "_initiator" => SimpleLoggerLogInitiators::WEB_USER,
768
- "failed_username" => $username,
769
- "server_http_user_agent" => isset( $_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null,
770
- // count all failed logins to unknown users as the same occasions,
771
- // to prevent log being flooded with login/hack attempts
772
- // "_occasionsID" => __CLASS__ . '/' . __FUNCTION__
773
- // Use same occasionsID as for failed login attempts to existing users,
774
- // because log can flood otherwise if hacker is rotating existing and non-existing usernames
775
- //"_occasionsID" => __CLASS__ . '/' . __FUNCTION__ . "/failed_user_login/userid:{$user->ID}"
776
- "_occasionsID" => __CLASS__ . '/failed_user_login',
777
- );
778
-
779
- /**
780
- * Maybe store password too
781
- * Default is to not do this because of privacy and security
782
- *
783
- * @since 2.0
784
- *
785
- * @param bool $log_password
786
- */
787
- $log_password = false;
788
- $log_password = apply_filters("simple_history/comments_logger/log_not_existing_user_password", $log_password);
789
- if ($log_password) {
790
- $context["failed_login_password"] = $password;
791
- }
792
-
793
- $this->warningMessage("user_unknown_login_failed", $context);
794
-
795
- }
796
-
797
- return $user;
798
-
799
- }
800
-
801
- /**
802
- * Add diff to array if old and new values are different
803
- *
804
- * Since 2.0.29
805
- */
806
- function add_diff($post_data_diff, $key, $old_value, $new_value) {
807
-
808
- if ( $old_value != $new_value ) {
809
-
810
- $post_data_diff[$key] = array(
811
- "old" => $old_value,
812
- "new" => $new_value
813
- );
814
-
815
- }
816
-
817
- return $post_data_diff;
818
-
819
- }
820
-
821
- /**
822
- * Return more info about an logged event
823
- * Supports so far:
824
- */
825
- function getLogRowDetailsOutput( $row ) {
826
-
827
- $context = $row->context;
828
- $message_key = $context["_message_key"];
829
-
830
- $out = "";
831
- $diff_table_output = "";
832
-
833
- if ( "user_updated_profile" == $message_key ) {
834
-
835
- // Find all user_prev_ and user_new_ values and show them
836
- $arr_user_keys_to_show_diff_for = array(
837
- "first_name" => array(
838
- "title" => _x("First name", "User logger", "simple-history")
839
- ),
840
- "last_name" => array(
841
- "title" => _x("Last name", "User logger", "simple-history")
842
- ),
843
- "nickname" => array(
844
- "title" => _x("Nickname", "User logger", "simple-history")
845
- ),
846
- "description" => array(
847
- "title" => _x("Description", "User logger", "simple-history"),
848
- ),
849
- "rich_editing" => array(
850
- "title" => _x("Visual editor", "User logger", "simple-history") // Disable visual editor
851
- ),
852
- "comment_shortcuts" => array(
853
- "title" => _x("Keyboard shortcuts", "User logger", "simple-history") // Enable keyboard shortcuts for comment moderation
854
- ),
855
- "show_admin_bar_front" => array(
856
- "title" => _x("Show Toolbar", "User logger", "simple-history") // Show Toolbar when viewing site
857
- ),
858
- "admin_color" => array(
859
- "title" => _x("Colour Scheme", "User logger", "simple-history") // Admin Colour Scheme
860
- ),
861
- "aim" => array(
862
- "title" => _x("AIM", "User logger", "simple-history")
863
- ),
864
- "yim" => array(
865
- "title" => _x("Yahoo IM", "User logger", "simple-history")
866
- ),
867
- "jabber" => array(
868
- "title" => _x("Jabber / Google Talk ", "User logger", "simple-history")
869
- ),
870
- /*"user_nicename" => array(
871
- "title" => _x("Nicename", "User logger", "simple-history")
872
- ),*/
873
- "user_email" => array(
874
- "title" => _x("Email", "User logger", "simple-history")
875
- ),
876
- "display_name" => array(
877
- //"title" => _x("Display name publicly as", "User logger", "simple-history")
878
- "title" => _x("Display name", "User logger", "simple-history")
879
- ),
880
- "user_url" => array(
881
- "title" => _x("Website", "User logger", "simple-history")
882
- ),
883
- "role" => array(
884
- //"title" => _x("Display name publicly as", "User logger", "simple-history")
885
- "title" => _x("Role", "User logger", "simple-history")
886
- )
887
- );
888
-
889
- foreach ( $arr_user_keys_to_show_diff_for as $key => $val ) {
890
-
891
- if ( isset( $context["user_prev_{$key}"] ) && isset( $context["user_new_{$key}"] ) ) {
892
-
893
- $user_old_value = $context["user_prev_{$key}"];
894
- $user_new_value = $context["user_new_{$key}"];
895
-
896
- $diff_table_output .= sprintf(
897
- '<tr>
898
- <td>%1$s</td>
899
- <td>%2$s</td>
900
- </tr>',
901
- $val["title"],
902
- sprintf(
903
- '<ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%1$s</ins> <del class="SimpleHistoryLogitem__keyValueTable__removedThing">%2$s</del>',
904
- esc_html( $user_new_value ), // 1
905
- esc_html( $user_old_value ) // 2
906
- )
907
- );
908
-
909
- }
910
-
911
- }
912
-
913
- // check if password was changed
914
- if ( isset( $context["edited_user_password_changed"] ) ) {
915
-
916
- $diff_table_output .= sprintf(
917
- '<tr>
918
- <td>%1$s</td>
919
- <td>%2$s</td>
920
- </tr>',
921
- _x("Password", "User logger", "simple-history"),
922
- _x("Changed", "User logger", "simple-history")
923
- );
924
-
925
- }
926
-
927
- if ( $diff_table_output ) {
928
- $diff_table_output = '<table class="SimpleHistoryLogitem__keyValueTable">' . $diff_table_output . '</table>';
929
- }
930
-
931
- $out .= $diff_table_output;
932
-
933
- } else if ( "user_created" == $message_key ) {
934
-
935
- // Show fields for created users
936
- $arr_user_keys_to_show_diff_for = array(
937
- "created_user_first_name" => array(
938
- "title" => _x("First name", "User logger", "simple-history")
939
- ),
940
- "created_user_last_name" => array(
941
- "title" => _x("Last name", "User logger", "simple-history")
942
- ),
943
- "created_user_url" => array(
944
- "title" => _x("Website", "User logger", "simple-history")
945
- ),
946
- "send_user_notification" => array(
947
- "title" => _x("User notification email sent", "User logger", "simple-history")
948
- )
949
- );
950
-
951
- foreach ( $arr_user_keys_to_show_diff_for as $key => $val ) {
952
-
953
- if ( isset( $context[ $key ] ) && trim( $context[ $key ] ) ) {
954
-
955
- if ( "send_user_notification" == $key ) {
956
-
957
- if ( intval( $context[ $key ] ) == 1 ) {
958
- $sent_status = _x("Yes, email with account details was sent", "User logger", "simple-history");
959
- } else {
960
- // $sent_status = _x("No, no email with account details was sent", "User logger", "simple-history");
961
- $sent_status = "";
962
- }
963
-
964
- if ( $sent_status ) {
965
-
966
- $diff_table_output .= sprintf(
967
- '<tr>
968
- <td>%1$s</td>
969
- <td>%2$s</td>
970
- </tr>',
971
- _x("Notification", "User logger", "simple-history"),
972
- sprintf(
973
- '<ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%1$s</ins>',
974
- esc_html( $sent_status ) // 1
975
- )
976
- );
977
-
978
- }
979
-
980
- } else {
981
-
982
- $diff_table_output .= sprintf(
983
- '<tr>
984
- <td>%1$s</td>
985
- <td>%2$s</td>
986
- </tr>',
987
- $val["title"],
988
- sprintf(
989
- '<ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%1$s</ins>',
990
- esc_html( $context[ $key ] ) // 1
991
- )
992
- );
993
-
994
- }
995
-
996
- }
997
-
998
- }
999
-
1000
- if ( $diff_table_output ) {
1001
- $diff_table_output = '<table class="SimpleHistoryLogitem__keyValueTable">' . $diff_table_output . '</table>';
1002
- }
1003
-
1004
- $out .= $diff_table_output;
1005
-
1006
- } // message key
1007
-
1008
- return $out;
1009
-
1010
-
1011
- }
1012
-
1013
  }
1
  <?php
2
 
 
 
3
  /**
4
  * Logs changes to user logins (and logouts)
5
  */
6
+ class SimpleUserLogger extends SimpleLogger
7
+ {
8
+
9
+ public $slug = __CLASS__;
10
+
11
+ /**
12
+ * Get array with information about this logger
13
+ *
14
+ * @return array
15
+ */
16
+ public function getInfo()
17
+ {
18
+
19
+ $arr_info = array(
20
+ "name" => __("User Logger", "simple-history"),
21
+ "description" => __("Logs user logins, logouts, and failed logins", "simple-history"),
22
+ "capability" => "edit_users",
23
+ "messages" => array(
24
+ 'user_login_failed' => __(
25
+ 'Failed to login with username "{login}" (incorrect password entered)',
26
+ "simple-history"
27
+ ),
28
+ 'user_unknown_login_failed' => __(
29
+ 'Failed to login with username "{failed_username}" (username does not exist)',
30
+ "simple-history"
31
+ ),
32
+ 'user_logged_in' => __('Logged in', "simple-history"),
33
+ 'user_unknown_logged_in' => __("Unknown user logged in", "simple-history"),
34
+ 'user_logged_out' => __("Logged out", "simple-history"),
35
+ 'user_updated_profile' => __(
36
+ "Edited the profile for user {edited_user_login} ({edited_user_email})",
37
+ "simple-history"
38
+ ),
39
+ 'user_created' => __(
40
+ "Created user {created_user_login} ({created_user_email}) with role {created_user_role}",
41
+ "simple-history"
42
+ ),
43
+ 'user_deleted' => __("Deleted user {deleted_user_login} ({deleted_user_email})", "simple-history"),
44
+ "user_password_reseted" => __("Reset their password", "simple-history"),
45
+ "user_requested_password_reset_link" => __(
46
+ "Requested a password reset link for user with login '{user_login}' and email '{user_email}'",
47
+ "simple-history"
48
+ ),
49
+
50
+ /*
51
+ Text used in admin:
52
+ Log Out of All Other Sessions
53
+ Left your account logged in at a public computer?
54
+ Lost your phone? This will log you out everywhere except your current browser
55
+ */
56
+ 'user_session_destroy_others' => _x(
57
+ 'Logged out from all other sessions',
58
+ 'User destroys other login sessions for themself',
59
+ 'simple-history'
60
+ ),
61
+ /*
62
+ Text used in admin:
63
+ 'Log %s out of all sessions' ), $profileuser->display_name );
64
+ */
65
+ 'user_session_destroy_everywhere' => _x(
66
+ 'Logged out "{user_display_name}" from all sessions',
67
+ 'User destroys all login sessions for a user',
68
+ 'simple-history'
69
+ ),
70
+ ),
71
+
72
+ "labels" => array(
73
+ "search" => array(
74
+ "label" => _x("Users", "User logger: search", "simple-history"),
75
+ "label_all" => _x("All user activity", "User logger: search", "simple-history"),
76
+ "options" => array(
77
+ _x("Successful user logins", "User logger: search", "simple-history") => array(
78
+ "user_logged_in",
79
+ "user_unknown_logged_in",
80
+ ),
81
+ _x("Failed user logins", "User logger: search", "simple-history") => array(
82
+ 'user_login_failed',
83
+ 'user_unknown_login_failed',
84
+ ),
85
+ _x('User logouts', 'User logger: search', 'simple-history') => array(
86
+ "user_logged_out",
87
+ ),
88
+ _x('Created users', 'User logger: search', 'simple-history') => array(
89
+ "user_created",
90
+ ),
91
+ _x("User profile updates", "User logger: search", "simple-history") => array(
92
+ "user_updated_profile",
93
+ ),
94
+ _x('User deletions', 'User logger: search', 'simple-history') => array(
95
+ "user_deleted",
96
+ ),
97
+
98
+ ),
99
+ ), // end search
100
+
101
+ ), // end labels
102
+
103
+ );
104
+
105
+ return $arr_info;
106
+ }
107
+
108
+ /**
109
+ * Add actions and filters when logger is loaded by Simple History
110
+ */
111
+ public function loaded()
112
+ {
113
+
114
+ // Plain logins and logouts
115
+ add_action("wp_login", array($this, "onWpLogin"), 10, 3);
116
+ add_action("wp_logout", array($this, "onWpLogout"));
117
+
118
+ // Failed login attempt to username that exists
119
+ add_action("wp_authenticate_user", array($this, "onWpAuthenticateUser"), 10, 2);
120
+
121
+ // Failed to login to user that did not exist (perhaps brute force)
122
+ // run this later than 10 because wordpress own email login check is done with prio 20
123
+ // so if we run at 10 we just get null
124
+ add_filter('authenticate', array($this, "onAuthenticate"), 30, 3);
125
+
126
+ // User is changed
127
+ #add_action("profile_update", array($this, "on_profile_update"), 10, 2);
128
+
129
+ // User is created
130
+ add_action("user_register", array($this, "onUserRegister"), 10, 2);
131
+
132
+ // User is deleted
133
+ add_action('delete_user', array($this, "onDeleteUser"), 10, 2);
134
+
135
+ // User sessions is destroyed. AJAX call that we hook onto early.
136
+ add_action("wp_ajax_destroy-sessions", array($this, "onDestroyUserSession"), 0);
137
+
138
+ // User reaches reset password (from link or only from user created link)
139
+ add_action('validate_password_reset', array($this, "onValidatePasswordReset" ), 10, 2);
140
+
141
+ add_action('retrieve_password_message', array($this, "onRetrievePasswordMessage" ), 10, 4);
142
+
143
+ add_filter('insert_user_meta', array($this, "onInsertUserMeta" ), 10, 3);
144
+ }
145
+
146
+ /*
147
+ * Called before the user is updated
148
+ *
149
+ * Filter a user's meta values and keys before the user is created or updated.
150
+ *
151
+ * Does not include contact methods. These are added using `wp_get_user_contact_methods($user )`.
152
+ *
153
+ * @param array $meta {
154
+ * Default meta values and keys for the user.
155
+ *
156
+ * @type string $nickname The user's nickname. Default is the user's username.
157
+ * @type string $first_name The user's first name.
158
+ * @type string $last_name The user's last name.
159
+ * @type string $description The user's description.
160
+ * @type bool $rich_editing Whether to enable the rich-editor for the user. False if not empty.
161
+ * @type bool $comment_shortcuts Whether to enable keyboard shortcuts for the user. Default false.
162
+ * @type string $admin_color The color scheme for a user's admin screen. Default 'fresh'.
163
+ * @type int|bool $use_ssl Whether to force SSL on the user's admin area. 0|false if SSL is
164
+ * not forced.
165
+ * @type bool $show_admin_bar_front Whether to show the admin bar on the front end for the user.
166
+ * Default true.
167
+ * }
168
+ * @param WP_User $user User object.
169
+ * @param bool $update Whether the user is being updated rather than created.
170
+ */
171
+ public function onInsertUserMeta($meta, $user, $update)
172
+ {
173
+
174
+ // We only log updates here
175
+ if (! $update) {
176
+ return $meta;
177
+ }
178
+
179
+ // $user should be set, but check just in case
180
+ if (empty($user) || ! is_object($user)) {
181
+ return $meta;
182
+ }
183
+
184
+ // Make of copy of the posted data, because we change the keys
185
+ $posted_data = $_POST;
186
+ $posted_data = stripslashes_deep($posted_data);
187
+
188
+ // Paranoid mode, just in case some other plugin fires the "insert_user_meta"
189
+ // filter and the user.php file is not loaded for some super wierd reason
190
+ if (! function_exists("_get_additional_user_keys")) {
191
+ return $meta;
192
+ }
193
+
194
+ // Get the default fields to include.
195
+ // This includes contact methods (including filter, so more could have been added)
196
+ $arr_keys_to_check = _get_additional_user_keys($user);
197
+
198
+ // Somehow some fields are not include above, so add them manually
199
+ $arr_keys_to_check = array_merge($arr_keys_to_check, array("user_email", "user_url", "display_name"));
200
+
201
+ // Skip some keys, because to much info or I don't know what they are
202
+ $arr_keys_to_check = array_diff($arr_keys_to_check, array("use_ssl"));
203
+
204
+ // Some keys have different ways of getting data from user
205
+ // so change posted object to match those
206
+ $posted_data["user_url"] = isset($posted_data["url"]) ? $posted_data["url"] : null;
207
+ $posted_data["show_admin_bar_front"] = isset($posted_data["admin_bar_front"]) ? true : null;
208
+ $posted_data["user_email"] = isset($posted_data["email"]) ? $posted_data["email"] : null;
209
+
210
+ // Display name publicly as = POST "display_name"
211
+ #var_dump($user->display_name);
212
+
213
+ // Set vals for Enable keyboard shortcuts for comment moderation
214
+ $posted_data['comment_shortcuts'] = isset($posted_data['comment_shortcuts']) ? "true" : "false";
215
+
216
+ // Set vals for Disable the visual editor when writing
217
+ // posted val = string "false" = yes, disable
218
+ $posted_data['rich_editing'] = isset($posted_data['rich_editing']) ? "false" : "true";
219
+
220
+ // Set vals for Show Toolbar when viewing site
221
+ $posted_data['show_admin_bar_front'] = isset($posted_data['admin_bar_front']) ? "true" : "false";
222
+
223
+ // if checkbox is checked in admin then this is the saved value on the user object
224
+ // @todo:
225
+
226
+ // Check if password was updated
227
+ $password_changed = false;
228
+ if (! empty($posted_data['pass1']) && ! empty($posted_data['pass2']) && $posted_data['pass1'] == $posted_data['pass2']) {
229
+ $password_changed = 1;
230
+ }
231
+
232
+ // Check if role was changed
233
+ //[role] => bbp_moderator
234
+ $role_changed = false;
235
+
236
+ // if user is network admin then role dropdown does not exist and role is not posted here
237
+ $new_role = isset($posted_data["role"]) ? $posted_data["role"] : null;
238
+
239
+ if ($new_role) {
240
+ // as done in user-edit.php
241
+ // Compare user role against currently editable roles
242
+ $user_roles = array_intersect(array_values($user->roles), array_keys(get_editable_roles()));
243
+ $old_role = reset($user_roles);
244
+
245
+ $role_changed = $new_role != $old_role;
246
+ }
247
+
248
+ // Will contain the differences
249
+ $user_data_diff = array();
250
+
251
+ // Check all keys for diff values
252
+ foreach ($arr_keys_to_check as $one_key_to_check) {
253
+ $old_val = $user->$one_key_to_check;
254
+ $new_val = isset($posted_data[$one_key_to_check]) ? $posted_data[$one_key_to_check] : null;
255
+
256
+ #echo "<hr>key: $one_key_to_check";
257
+ #echo "<br>old val: $old_val";
258
+ #echo "<br>new val: $new_val";
259
+
260
+ // new val must be set, because otherwise we are not setting anything
261
+ if (! isset($new_val)) {
262
+ continue;
263
+ }
264
+
265
+ $user_data_diff = $this->addDiff($user_data_diff, $one_key_to_check, $old_val, $new_val);
266
+ }
267
+
268
+ // Setup basic context
269
+ $context = array(
270
+ "edited_user_id" => $user->ID,
271
+ "edited_user_email" => $user->user_email,
272
+ "edited_user_login" => $user->user_login,
273
+ "server_http_user_agent" => isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : null,
274
+ );
275
+
276
+ if ($password_changed) {
277
+ $context["edited_user_password_changed"] = "1";
278
+ }
279
+
280
+ if ($role_changed) {
281
+ $context["user_prev_role"] = $old_role;
282
+ $context["user_new_role"] = $new_role;
283
+ }
284
+
285
+ // Add diff to context
286
+ if ($user_data_diff) {
287
+ foreach ($user_data_diff as $one_diff_key => $one_diff_vals) {
288
+ /*
289
+ One diff looks like:
290
+ "nickname": {
291
+ "old": "MyOldNick",
292
+ "new": "MyNewNick"
293
+ }
294
+ */
295
+ $context["user_prev_{$one_diff_key}"] = $one_diff_vals["old"];
296
+ $context["user_new_{$one_diff_key}"] = $one_diff_vals["new"];
297
+ }
298
+ }
299
+
300
+
301
+ $this->infoMessage("user_updated_profile", $context);
302
+
303
+ return $meta;
304
+ }
305
+
306
+ /**
307
+ *
308
+ * user requests a reset password link
309
+ *
310
+ */
311
+ public function onRetrievePasswordMessage($message, $key, $user_login, $user_data)
312
+ {
313
+
314
+ if (isset($_GET["action"]) && ("lostpassword" == $_GET["action"])) {
315
+ $context = array(
316
+ "_initiator" => SimpleLoggerLogInitiators::WEB_USER,
317
+ "message" => $message,
318
+ "key" => $key,
319
+ "user_login" => $user_login,
320
+ );
321
+
322
+ if (is_a($user_data, "WP_User")) {
323
+ $context["user_email"] = $user_data->user_email;
324
+ }
325
+
326
+ $this->noticeMessage("user_requested_password_reset_link", $context);
327
+ }
328
+
329
+ return $message;
330
+ }
331
+
332
+ /**
333
+ * Fired before the password reset procedure is validated.
334
+ *
335
+ * @param object $errors WP Error object.
336
+ * @param WP_User|WP_Error $user WP_User object if the login and reset key match. WP_Error object otherwise.
337
+ */
338
+ public function onValidatePasswordReset($errors, $user)
339
+ {
340
+
341
+ /*
342
+ User visits the forgot password screen
343
+ $errors object are empty
344
+ $user contains a user
345
+ $_post is empty
346
+
347
+ User resets password
348
+ $errors empty
349
+ $user user object
350
+ $_post
351
+
352
+ */
353
+
354
+ $context = array();
355
+
356
+ if (is_a($user, "WP_User")) {
357
+ $context["_initiator"] = SimpleLoggerLogInitiators::WP_USER;
358
+ $context["_user_id"] = $user->ID;
359
+ $context["_user_login"] = $user->user_login;
360
+ $context["_user_email"] = $user->user_email;
361
+ }
362
+
363
+ if (isset($_POST['pass1']) && $_POST['pass1'] != $_POST['pass2']) {
364
+ // $errors->add( 'password_reset_mismatch', __( 'The passwords do not match.' ) );
365
+ // user failed to reset password
366
+ }
367
+
368
+
369
+ if ((! $errors->get_error_code()) && isset($_POST['pass1']) && !empty($_POST['pass1'])) {
370
+ // login_header( __( 'Password Reset' ), '<p class="message reset-pass">'
371
+ // . __( 'Your password has been reset.' ) . ' <a href="' . esc_url(
372
+ $this->infoMessage("user_password_reseted", $context);
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Called when user dessions are destroyed from admin
378
+ * Can be called for current logged in user = destroy all other sessions
379
+ * or for another user = destroy alla sessions for that user
380
+ * Fires from AJAX call
381
+ *
382
+ * @since 2.0.6
383
+ */
384
+ public function onDestroyUserSession()
385
+ {
386
+
387
+ /*
388
+ Post params:
389
+ nonce: a14df12195
390
+ user_id: 1
391
+ action: destroy-sessions
392
+ */
393
+
394
+ $user = get_userdata((int) $_POST['user_id']);
395
+
396
+ if ($user) {
397
+ if (!current_user_can('edit_user', $user->ID)) {
398
+ $user = false;
399
+ } elseif (!wp_verify_nonce($_POST['nonce'], 'update-user_' . $user->ID)) {
400
+ $user = false;
401
+ }
402
+ }
403
+
404
+ if (!$user) {
405
+ // Could not log out user sessions. Please try again.
406
+ return;
407
+ }
408
+
409
+ $sessions = WP_Session_Tokens::get_instance($user->ID);
410
+
411
+ $context = array();
412
+
413
+ if ($user->ID === get_current_user_id()) {
414
+ $this->infoMessage("user_session_destroy_others");
415
+ } else {
416
+ $context["user_id"] = $user->ID;
417
+ $context["user_login"] = $user->user_login;
418
+ $context["user_display_name"] = $user->display_name;
419
+
420
+ $this->infoMessage("user_session_destroy_everywhere", $context);
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Fires before a user is deleted from the database.
426
+ *
427
+ * @param int $user_id ID of the deleted user.
428
+ * @param int|null $reassign ID of the user to reassign posts and links to.
429
+ * Default null, for no reassignment.
430
+ */
431
+ public function onDeleteUser($user_id, $reassign)
432
+ {
433
+
434
+ $wp_user_to_delete = get_userdata($user_id);
435
+
436
+ // wp_user->roles (array) - the roles the user is part of.
437
+ $role = null;
438
+ if (is_array($wp_user_to_delete->roles) && !empty($wp_user_to_delete->roles[0])) {
439
+ $role = $wp_user_to_delete->roles[0];
440
+ }
441
+
442
+ $context = array(
443
+ "deleted_user_id" => $wp_user_to_delete->ID,
444
+ "deleted_user_email" => $wp_user_to_delete->user_email,
445
+ "deleted_user_login" => $wp_user_to_delete->user_login,
446
+ "deleted_user_role" => $role,
447
+ "reassign_user_id" => $reassign,
448
+ "server_http_user_agent" => isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : null
449
+ );
450
+
451
+ // Let's log this as a little bit more significant that just "message"
452
+ $this->noticeMessage("user_deleted", $context);
453
+ }
454
+
455
+ /**
456
+ * Modify plain text row output
457
+ * - adds link to user profil
458
+ * - change to "your profile" if you're looking at your own edit
459
+ */
460
+ public function getLogRowPlainTextOutput($row)
461
+ {
462
+
463
+ $context = $row->context;
464
+
465
+ $output = parent::getLogRowPlainTextOutput($row);
466
+ $current_user_id = get_current_user_id();
467
+
468
+ if ("user_updated_profile" == $context["_message_key"]) {
469
+ $wp_user = get_user_by("id", $context["edited_user_id"]);
470
+
471
+ // If edited_user_id and _user_id is the same then a user edited their own profile
472
+ // Note: it's not the same thing as the currently logged in user (but.. it can be!)
473
+ if (! empty($context["_user_id"]) && $context["edited_user_id"] === $context["_user_id"]) {
474
+ if ($wp_user) {
475
+ $context["edit_profile_link"] = get_edit_user_link($wp_user->ID);
476
+
477
+ $use_you = apply_filters("simple_history/user_logger/plain_text_output_use_you", true);
478
+
479
+ //error_log( serialize($current_user_id) ); // int 1
480
+ //error_log( serialize($context["_user_id"]) ); // string 1
481
+
482
+ // User still exist, so link to their profile
483
+ if ((int) $current_user_id === (int) $context["_user_id"] && $use_you) {
484
+ // User that is viewing the log is the same as the edited user
485
+ $msg = __('Edited <a href="{edit_profile_link}">your profile</a>', "simple-history");
486
+ } else {
487
+ $msg = __('Edited <a href="{edit_profile_link}">their profile</a>', "simple-history");
488
+ }
489
+
490
+ $output = $this->interpolate($msg, $context, $row);
491
+ } else {
492
+ // User does not exist any longer
493
+ $output = __("Edited your profile", "simple-history");
494
+ }
495
+ } else {
496
+ // User edited another users profile
497
+ if ($wp_user) {
498
+ // Edited user still exist, so link to their profile
499
+ $context["edit_profile_link"] = get_edit_user_link($wp_user->ID);
500
+ $msg = __('Edited the profile for user <a href="{edit_profile_link}">{edited_user_login} ({edited_user_email})</a>', "simple-history");
501
+ $output = $this->interpolate($msg, $context, $row);
502
+ } else {
503
+ // Edited user does not exist any longer
504
+ }
505
+ }
506
+ // if user_updated_profile
507
+ } elseif ("user_created" == $context["_message_key"]) {
508
+ // A user was created. Create link of username that goes to user profile.
509
+ $wp_user = get_user_by("id", $context["created_user_id"]);
510
+
511
+ // If edited_user_id and _user_id is the same then a user edited their own profile
512
+ // Note: it's not the same thing as the currently logged in user (but.. it can be!)
513
+
514
+ if ($wp_user) {
515
+ $context["edit_profile_link"] = get_edit_user_link($wp_user->ID);
516
+
517
+ // User that is viewing the log is the same as the edited user
518
+ $msg = __(
519
+ 'Created user <a href="{edit_profile_link}">{created_user_login} ({created_user_email})</a> with role {created_user_role}',
520
+ "simple-history"
521
+ );
522
+
523
+ $output = $this->interpolate(
524
+ $msg,
525
+ $context,
526
+ $row
527
+ );
528
+ } else {
529
+ // User does not exist any longer, keep original message
530
+ }
531
+ }
532
+
533
+ return $output;
534
+ }
535
+
536
+ /**
537
+ * User logs in
538
+ *
539
+ * @param string $user_login
540
+ * @param object $user
541
+ */
542
+ public function onWpLogin($user_login = null, $user = null)
543
+ {
544
+
545
+ $context = array(
546
+ "user_login" => $user_login
547
+ );
548
+
549
+ if (isset($user_login)) {
550
+ $user_obj = get_user_by("login", $user_login);
551
+ } elseif (isset($user) && isset($user->ID)) {
552
+ $user_obj = get_user_by("id", $user->ID);
553
+ }
554
+
555
+ if (is_a($user_obj, "WP_User")) {
556
+ $context = array(
557
+ "user_id" => $user_obj->ID,
558
+ "user_email" => $user_obj->user_email,
559
+ "user_login" => $user_obj->user_login,
560
+ );
561
+
562
+ // Override some data that is usually set automagically by Simple History
563
+ // Because wp_get_current_user() does not return any data yet at this point
564
+ $context["_initiator"] = SimpleLoggerLogInitiators::WP_USER;
565
+ $context["_user_id"] = $user_obj->ID;
566
+ $context["_user_login"] = $user_obj->user_login;
567
+ $context["_user_email"] = $user_obj->user_email;
568
+ $context["server_http_user_agent"] = isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : null;
569
+
570
+ $this->infoMessage("user_logged_in", $context);
571
+ } else {
572
+ // Could not get any info about the user logging in
573
+ $this->warningMessage("user_unknown_logged_in", $context);
574
+ }
575
+ }
576
+
577
+ /**
578
+ * User logs out
579
+ * http://codex.wordpress.org/Plugin_API/Action_Reference/wp_logout
580
+ */
581
+ public function onWpLogout()
582
+ {
583
+ $this->infoMessage("user_logged_out");
584
+ }
585
+
586
+ /**
587
+ * User is edited
588
+ *
589
+ * Called immediately after an existing user is updated.
590
+ * @param int $user_id User ID.
591
+ * @param object $old_user_data Object containing user's data prior to update.
592
+ */
593
+ // public function on_profile_update($user_id, $old_user_data) {
594
+ /*
595
+ if (!$user_id || !is_numeric($user_id)) {
596
+ return;
597
+ }
598
+
599
+ $wp_user_edited = get_userdata($user_id);
600
+
601
+ $context = array(
602
+ "edited_user_id" => $wp_user_edited->ID,
603
+ "edited_user_email" => $wp_user_edited->user_email,
604
+ "edited_user_login" => $wp_user_edited->user_login,
605
+ "server_http_user_agent" => isset($_SERVER["HTTP_USER_AGENT"] ) ? $_SERVER["HTTP_USER_AGENT"] : null,
606
+ "old_user_data" => $old_user_data
607
+ );
608
+
609
+
610
+ $this->infoMessage("user_updated_profile", $context);
611
+ */
612
+ // }
613
+
614
+ /**
615
+ * User is created
616
+ *
617
+ * "This action hook allows you to access data for a new user immediately after they are added to the database.
618
+ * The user id is passed to hook as an argument."
619
+ *
620
+ */
621
+ public function onUserRegister($user_id)
622
+ {
623
+
624
+ if (! $user_id || ! is_numeric($user_id)) {
625
+ return;
626
+ }
627
+
628
+ $wp_user_added = get_userdata($user_id);
629
+
630
+ // wp_user->roles (array) - the roles the user is part of.
631
+ $role = null;
632
+ if (is_array($wp_user_added->roles) && ! empty($wp_user_added->roles[0])) {
633
+ $role = $wp_user_added->roles[0];
634
+ }
635
+
636
+ $send_user_notification = (int) (isset($_POST["send_user_notification"]) && $_POST["send_user_notification"] );
637
+
638
+ $context = array(
639
+ "created_user_id" => $wp_user_added->ID,
640
+ "created_user_email" => $wp_user_added->user_email,
641
+ "created_user_login" => $wp_user_added->user_login, // username
642
+ "created_user_role" => $role,
643
+ "created_user_first_name" => $wp_user_added->first_name,
644
+ "created_user_last_name" => $wp_user_added->last_name,
645
+ "created_user_url" => $wp_user_added->user_url,
646
+ "send_user_notification" => $send_user_notification,
647
+ "server_http_user_agent" => isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : null
648
+ );
649
+
650
+ $this->infoMessage("user_created", $context);
651
+ }
652
+
653
+ /**
654
+ * Log failed login attempt to username that exists
655
+ *
656
+ * @param WP_User or WP_Error
657
+ * $user The WP_User() object of the user being edited,
658
+ * or a WP_Error() object if validation has already failed.
659
+ * @param string password used
660
+ */
661
+ public function onWpAuthenticateUser($userOrError, $password)
662
+ {
663
+
664
+ // Only continue if $userOrError is a WP_user object
665
+ if (! is_a($userOrError, "WP_User")) {
666
+ return $userOrError;
667
+ }
668
+
669
+ // Only log failed attempts
670
+ if (! wp_check_password($password, $userOrError->user_pass, $userOrError->ID)) {
671
+ // Overwrite some vars that Simple History set automagically
672
+ $context = array(
673
+ "_initiator" => SimpleLoggerLogInitiators::WEB_USER,
674
+ "login_id" => $userOrError->ID,
675
+ "login_email" => $userOrError->user_email,
676
+ "login" => $userOrError->user_login,
677
+ "server_http_user_agent" => isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : null,
678
+ "_occasionsID" => __CLASS__ . '/failed_user_login',
679
+ );
680
+
681
+ /**
682
+ * Maybe store password too
683
+ * Default is to not do this because of privacy and security
684
+ *
685
+ * @since 2.0
686
+ *
687
+ * @param bool $log_password
688
+ */
689
+ $log_password = false;
690
+ $log_password = apply_filters("simple_history/comments_logger/log_failed_password", $log_password);
691
+
692
+ if ($log_password) {
693
+ $context["login_user_password"] = $password;
694
+ }
695
+
696
+ $this->warningMessage("user_login_failed", $context);
697
+ }
698
+
699
+ return $userOrError;
700
+ }
701
+
702
+ /**
703
+ * Attempt to login to user that does not exist
704
+ *
705
+ * @param $user (null or WP_User or WP_Error) (required)
706
+ * null indicates no process has authenticated the user yet.
707
+ * A WP_Error object indicates another process has failed the authentication.
708
+ * A WP_User object indicates another process has authenticated the user.
709
+ * @param $username The user's username. since 4.5.0 `$username` now accepts an email address.
710
+ * @param $password The user's password (encrypted)
711
+ */
712
+ public function onAuthenticate($user, $username, $password)
713
+ {
714
+
715
+ // Don't log empty usernames
716
+ if (! trim($username)) {
717
+ return $user;
718
+ }
719
+
720
+ // If null then no auth done yet. Wierd. But what can we do.
721
+ if (is_null($user)) {
722
+ return $user;
723
+ }
724
+
725
+ // If auth ok then $user is a wp_user object
726
+ if (is_a($user, 'WP_User')) {
727
+ return $user;
728
+ }
729
+
730
+ // If user is a WP_Error object then auth failed
731
+ // Error codes can be:
732
+ // "incorrect_password" | "empty_password" | "invalid_email" | "invalid_username"
733
+ // We only act on invalid emails and invalid usernames
734
+ if (is_a($user, 'WP_Error') && ($user->get_error_code() == "invalid_username" || $user->get_error_code() == "invalid_email")) {
735
+ $context = array(
736
+ "_initiator" => SimpleLoggerLogInitiators::WEB_USER,
737
+ "failed_username" => $username,
738
+ "server_http_user_agent" => isset($_SERVER["HTTP_USER_AGENT"]) ? $_SERVER["HTTP_USER_AGENT"] : null,
739
+ // count all failed logins to unknown users as the same occasions,
740
+ // to prevent log being flooded with login/hack attempts
741
+ // "_occasionsID" => __CLASS__ . '/' . __FUNCTION__
742
+ // Use same occasionsID as for failed login attempts to existing users,
743
+ // because log can flood otherwise if hacker is rotating existing and non-existing usernames
744
+ //"_occasionsID" => __CLASS__ . '/' . __FUNCTION__ . "/failed_user_login/userid:{$user->ID}"
745
+ "_occasionsID" => __CLASS__ . '/failed_user_login',
746
+ );
747
+
748
+ /**
749
+ * Maybe store password too
750
+ * Default is to not do this because of privacy and security
751
+ *
752
+ * @since 2.0
753
+ *
754
+ * @param bool $log_password
755
+ */
756
+ $log_password = false;
757
+ $log_password = apply_filters(
758
+ "simple_history/comments_logger/log_not_existing_user_password",
759
+ $log_password
760
+ );
761
+ if ($log_password) {
762
+ $context["failed_login_password"] = $password;
763
+ }
764
+
765
+ $this->warningMessage("user_unknown_login_failed", $context);
766
+ }
767
+
768
+ return $user;
769
+ }
770
+
771
+ /**
772
+ * Add diff to array if old and new values are different
773
+ *
774
+ * Since 2.0.29
775
+ */
776
+ public function addDiff($post_data_diff, $key, $old_value, $new_value)
777
+ {
778
+ if ($old_value != $new_value) {
779
+ $post_data_diff[$key] = array(
780
+ "old" => $old_value,
781
+ "new" => $new_value
782
+ );
783
+ }
784
+
785
+ return $post_data_diff;
786
+ }
787
+
788
+ /**
789
+ * Return more info about an logged event
790
+ * Supports so far:
791
+ */
792
+ public function getLogRowDetailsOutput($row)
793
+ {
794
+ $context = $row->context;
795
+ $message_key = $context["_message_key"];
796
+
797
+ $out = "";
798
+ $diff_table_output = "";
799
+
800
+ if ("user_updated_profile" == $message_key) {
801
+ // Find all user_prev_ and user_new_ values and show them
802
+ $arr_user_keys_to_show_diff_for = array(
803
+ "first_name" => array(
804
+ "title" => _x("First name", "User logger", "simple-history")
805
+ ),
806
+ "last_name" => array(
807
+ "title" => _x("Last name", "User logger", "simple-history")
808
+ ),
809
+ "nickname" => array(
810
+ "title" => _x("Nickname", "User logger", "simple-history")
811
+ ),
812
+ "description" => array(
813
+ "title" => _x("Description", "User logger", "simple-history"),
814
+ ),
815
+ "rich_editing" => array(
816
+ // Disable visual editor
817
+ "title" => _x("Visual editor", "User logger", "simple-history")
818
+ ),
819
+ "comment_shortcuts" => array(
820
+ // Enable keyboard shortcuts for comment moderation
821
+ "title" => _x("Keyboard shortcuts", "User logger", "simple-history")
822
+ ),
823
+ "show_admin_bar_front" => array(
824
+ // Show Toolbar when viewing site
825
+ "title" => _x("Show Toolbar", "User logger", "simple-history")
826
+ ),
827
+ "admin_color" => array(
828
+ // Admin Colour Scheme
829
+ "title" => _x("Colour Scheme", "User logger", "simple-history")
830
+ ),
831
+ "aim" => array(
832
+ "title" => _x("AIM", "User logger", "simple-history")
833
+ ),
834
+ "yim" => array(
835
+ "title" => _x("Yahoo IM", "User logger", "simple-history")
836
+ ),
837
+ "jabber" => array(
838
+ "title" => _x("Jabber / Google Talk ", "User logger", "simple-history")
839
+ ),
840
+ /*"user_nicename" => array(
841
+ "title" => _x("Nicename", "User logger", "simple-history")
842
+ ),*/
843
+ "user_email" => array(
844
+ "title" => _x("Email", "User logger", "simple-history")
845
+ ),
846
+ "display_name" => array(
847
+ //"title" => _x("Display name publicly as", "User logger", "simple-history")
848
+ "title" => _x("Display name", "User logger", "simple-history")
849
+ ),
850
+ "user_url" => array(
851
+ "title" => _x("Website", "User logger", "simple-history")
852
+ ),
853
+ "role" => array(
854
+ //"title" => _x("Display name publicly as", "User logger", "simple-history")
855
+ "title" => _x("Role", "User logger", "simple-history")
856
+ )
857
+ );
858
+
859
+ foreach ($arr_user_keys_to_show_diff_for as $key => $val) {
860
+ if (isset($context["user_prev_{$key}"]) && isset($context["user_new_{$key}"])) {
861
+ $user_old_value = $context["user_prev_{$key}"];
862
+ $user_new_value = $context["user_new_{$key}"];
863
+
864
+ $diff_table_output .= sprintf(
865
+ '<tr>
866
+ <td>%1$s</td>
867
+ <td>%2$s</td>
868
+ </tr>',
869
+ $val["title"],
870
+ sprintf(
871
+ '<ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%1$s</ins> <del class="SimpleHistoryLogitem__keyValueTable__removedThing">%2$s</del>',
872
+ esc_html($user_new_value), // 1
873
+ esc_html($user_old_value) // 2
874
+ )
875
+ );
876
+ }
877
+ }
878
+
879
+ // check if password was changed
880
+ if (isset($context["edited_user_password_changed"])) {
881
+ $diff_table_output .= sprintf(
882
+ '<tr>
883
+ <td>%1$s</td>
884
+ <td>%2$s</td>
885
+ </tr>',
886
+ _x("Password", "User logger", "simple-history"),
887
+ _x("Changed", "User logger", "simple-history")
888
+ );
889
+ }
890
+
891
+ if ($diff_table_output) {
892
+ $diff_table_output = '<table class="SimpleHistoryLogitem__keyValueTable">' . $diff_table_output . '</table>';
893
+ }
894
+
895
+ $out .= $diff_table_output;
896
+ } elseif ("user_created" == $message_key) {
897
+ // Show fields for created users
898
+ $arr_user_keys_to_show_diff_for = array(
899
+ "created_user_first_name" => array(
900
+ "title" => _x("First name", "User logger", "simple-history")
901
+ ),
902
+ "created_user_last_name" => array(
903
+ "title" => _x("Last name", "User logger", "simple-history")
904
+ ),
905
+ "created_user_url" => array(
906
+ "title" => _x("Website", "User logger", "simple-history")
907
+ ),
908
+ "send_user_notification" => array(
909
+ "title" => _x("User notification email sent", "User logger", "simple-history")
910
+ )
911
+ );
912
+
913
+ foreach ($arr_user_keys_to_show_diff_for as $key => $val) {
914
+ if (isset($context[$key]) && trim($context[$key])) {
915
+ if ("send_user_notification" == $key) {
916
+ if (intval($context[$key]) == 1) {
917
+ $sent_status = _x(
918
+ "Yes, email with account details was sent",
919
+ "User logger",
920
+ "simple-history"
921
+ );
922
+ } else {
923
+ // $sent_status =
924
+ // _x("No, no email with account details was sent", "User logger", "simple-history");
925
+ $sent_status = "";
926
+ }
927
+
928
+ if ($sent_status) {
929
+ $diff_table_output .= sprintf(
930
+ '<tr>
931
+ <td>%1$s</td>
932
+ <td>%2$s</td>
933
+ </tr>',
934
+ _x("Notification", "User logger", "simple-history"),
935
+ sprintf(
936
+ '<ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%1$s</ins>',
937
+ esc_html($sent_status) // 1
938
+ )
939
+ );
940
+ }
941
+ } else {
942
+ $diff_table_output .= sprintf(
943
+ '<tr>
944
+ <td>%1$s</td>
945
+ <td>%2$s</td>
946
+ </tr>',
947
+ $val["title"],
948
+ sprintf(
949
+ '<ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%1$s</ins>',
950
+ esc_html($context[$key]) // 1
951
+ )
952
+ );
953
+ }
954
+ }
955
+ }
956
+
957
+ if ($diff_table_output) {
958
+ $diff_table_output = '<table class="SimpleHistoryLogitem__keyValueTable">' . $diff_table_output . '</table>';
959
+ }
960
+
961
+ $out .= $diff_table_output;
962
+ } // message key
963
+
964
+ return $out;
965
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
966
  }
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: eskapism
3
  Donate link: http://eskapism.se/sida/donate/
4
  Tags: history, log, changes, changelog, audit, trail, pages, attachments, users, dashboard, admin, syslog, feed, activity, stream, audit trail, brute-force
5
  Requires at least: 4.5.1
6
- Tested up to: 4.6
7
- Stable tag: 2.13
8
 
9
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
10
 
@@ -60,6 +60,11 @@ Simple History logging login attempts, lockouts, and configuration changes made
60
  The [redirection plugin](https://sv.wordpress.org/plugins/redirection/) manages url redirections, using a nice GUI.
61
  Simple History will log redirects and groups that are created, changed, enabled or disabled and also when the global plugin settings have been modified.
62
 
 
 
 
 
 
63
  #### RSS feed available
64
 
65
  There is also a **RSS feed of changes** available, so you can keep track of the changes made via your favorite RSS reader on your phone, on your iPad, or on your computer.
@@ -157,6 +162,15 @@ A simple way to see any uncommon activity, for example an increased number of lo
157
 
158
  ## Changelog
159
 
 
 
 
 
 
 
 
 
 
160
  = 2.13 (November 2016) =
161
 
162
  - Added filter `simple_history_log` that is a simplified way to add message to the log, without the need to check for the existance of Simple History or its SimpleLogger function. Use it like this: `apply_filters("simple_history_log", "This is a logged message");` See the [examples file](https://github.com/bonny/WordPress-Simple-History/blob/master/examples/examples.php) for more examples.
3
  Donate link: http://eskapism.se/sida/donate/
4
  Tags: history, log, changes, changelog, audit, trail, pages, attachments, users, dashboard, admin, syslog, feed, activity, stream, audit trail, brute-force
5
  Requires at least: 4.5.1
6
+ Tested up to: 4.7
7
+ Stable tag: 2.14
8
 
9
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
10
 
60
  The [redirection plugin](https://sv.wordpress.org/plugins/redirection/) manages url redirections, using a nice GUI.
61
  Simple History will log redirects and groups that are created, changed, enabled or disabled and also when the global plugin settings have been modified.
62
 
63
+ **Duplicate Post**
64
+ Tjhe plugin [Duplicate Post](https://wordpress.org/plugins/duplicate-post/) allows users to
65
+ clone posts of any type.
66
+ Simple History will log when a clone of a post or page is done.
67
+
68
  #### RSS feed available
69
 
70
  There is also a **RSS feed of changes** available, so you can keep track of the changes made via your favorite RSS reader on your phone, on your iPad, or on your computer.
162
 
163
  ## Changelog
164
 
165
+ = 2.14 (April 2016) =
166
+
167
+ - Added support for plugin [Duplicate Post](https://wordpress.org/plugins/duplicate-post/).
168
+ Now when a user clones a post or page you will se this in the history log, with links to both the original post and the new copy.
169
+ - Removed log level info from title in RSS feed
170
+ - Make date dropdown less "jumpy" when loading page (due to select element switching to Select2)
171
+ - Only add filters for plugin Limit Login Attempts if plugin is active. This fixes problem with Limit Login Attempts Reloaded and possibly other forks of the plugin.
172
+ - Debug page now displays installed plugins.
173
+
174
  = 2.13 (November 2016) =
175
 
176
  - Added filter `simple_history_log` that is a simplified way to add message to the log, without the need to check for the existance of Simple History or its SimpleLogger function. Use it like this: `apply_filters("simple_history_log", "This is a logged message");` See the [examples file](https://github.com/bonny/WordPress-Simple-History/blob/master/examples/examples.php) for more examples.
templates/template-settings-tab-debug.php CHANGED
@@ -1,6 +1,11 @@
1
  <?php
 
 
 
 
 
2
 
3
- defined( 'ABSPATH' ) or die();
4
 
5
  global $wpdb;
6
 
@@ -15,9 +20,9 @@ $period_end_date = DateTime::createFromFormat( 'U', time() );
15
  * Size of database in both number or rows and table size
16
  */
17
 
18
- echo "<h3>Database size</h3>";
19
 
20
- // Get table sizes in mb
21
  $sql_table_size = sprintf( '
22
  SELECT table_name AS "table_name",
23
  round(((data_length + index_length) / 1024 / 1024), 2) "size_in_mb"
@@ -256,3 +261,42 @@ foreach ( $logger_rows_count as $one_logger_slug => $one_logger_val ) {
256
  }
257
 
258
  echo "</table>";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ /**
3
+ * Undocumented class
4
+ *
5
+ * @package SimpleHistory
6
+ **/
7
 
8
+ defined( 'ABSPATH' ) || die();
9
 
10
  global $wpdb;
11
 
20
  * Size of database in both number or rows and table size
21
  */
22
 
23
+ echo '<h3>Database size</h3>';
24
 
25
+ // Get table sizes in mb.
26
  $sql_table_size = sprintf( '
27
  SELECT table_name AS "table_name",
28
  round(((data_length + index_length) / 1024 / 1024), 2) "size_in_mb"
261
  }
262
 
263
  echo "</table>";
264
+
265
+ // List installed plugins
266
+ echo '<h2>Plugins</h2>';
267
+ echo '<p>As returned from <code>get_plugins()</code></p>';
268
+
269
+ $plugins = get_plugins();
270
+
271
+ echo "<table class='widefat'>";
272
+ printf(
273
+ '<thead>
274
+ <tr>
275
+ <th>%1$s</th>
276
+ <th>%2$s</th>
277
+ <th>%3$s</th>
278
+ </tr>
279
+ </thead>
280
+ ',
281
+ _x("Plugin name", "debug dropin", "simple-history"),
282
+ _x("Plugin file path", "debug dropin", "simple-history"),
283
+ _x("Active", "debug dropin", "simple-history")
284
+ );
285
+
286
+ foreach ($plugins as $pluginFilePath => $onePlugin) {
287
+ $isPluginActive = is_plugin_active($pluginFilePath);
288
+ printf(
289
+ '
290
+ <tr>
291
+ <td><strong>%1$s</strong></td>
292
+ <td>%2$s</td>
293
+ <td>%3$s</td>
294
+ </tr>
295
+ ',
296
+ esc_html($onePlugin["Name"]),
297
+ esc_html($pluginFilePath),
298
+ $isPluginActive ? "Yes" : "No" // 3
299
+ );
300
+ }
301
+
302
+ echo "</table>";