Simple History - Version 2.4

Version Description

(November 2015) =

  • Added: Now logs when a user changes their password using the "reset password" link.
  • Added: Now logs when a user uses the password reset form.
  • Added: New method register_dropin that can be used to add dropins.
  • Added: New action simple_history/add_custom_dropin.
  • Added: Example on how to add an external dropin: example-dropin.php.
  • Added: "Last day" added to filter, because a brute force attack can add so many logs that it's not possible to fetch a whole week.
  • Changed: Filter simple_history/log/do_log now pass 5 arguments instead of 3. Before this update in was not possible for multiple add_action()-calls to use this filter, because you would not now if any other code had canceled it and so on. If you have been using this filter you need to modify your code.
  • Changed: When hovering the time of an event in the log, the date of the event displays in both local time and GMT time. Hopefully makes it easier for admins in different timezones that work together on a site to understand when each event happened. Fixes https://github.com/bonny/WordPress-Simple-History/issues/84.
  • Fixed: Line height was a bit tight on the dashboard. Also: the margin was a tad to small for the first logged event on the dashboard.
  • Fixed: Username was not added correctly to failed login attempts when using plugin Captcha on Login + it would still show that a user logged out sometimes when a bot/script brute force attacked a site by only sending login and password and not the captcha field.
Download this release

Release Info

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

Code changes from version 2.3.1 to 2.4

css/styles.css CHANGED
@@ -502,7 +502,7 @@ i.e. the log is inside a .postbox element
502
  }
503
 
504
  .postbox .SimpleHistoryLogitem:first-child {
505
- padding-top: 0;
506
  }
507
 
508
  .postbox .SimpleHistoryLogitem::before {
@@ -536,7 +536,7 @@ i.e. the log is inside a .postbox element
536
  .postbox .SimpleHistoryLogitem__text,
537
  .postbox .SimpleHistoryLogitem__details,
538
  .postbox .SimpleHistoryLogitem__details p {
539
- line-height: 1.1;
540
  }
541
 
542
  .postbox .SimpleHistoryPaginationLink,
502
  }
503
 
504
  .postbox .SimpleHistoryLogitem:first-child {
505
+ /*padding-top: 0;*/
506
  }
507
 
508
  .postbox .SimpleHistoryLogitem::before {
536
  .postbox .SimpleHistoryLogitem__text,
537
  .postbox .SimpleHistoryLogitem__details,
538
  .postbox .SimpleHistoryLogitem__details p {
539
+ line-height: 1.5;
540
  }
541
 
542
  .postbox .SimpleHistoryPaginationLink,
dropins/SimpleHistoryFilterDropin.php CHANGED
@@ -127,6 +127,10 @@ class SimpleHistoryFilterDropin {
127
  $numEvents = $this->get_unique_events_for_days($daysToShow);
128
  $numPages = $numEvents / $this->sh->get_pager_size();
129
 
 
 
 
 
130
  if ( $numPages < 20 ) {
131
 
132
  // Not that many things the last 7 days. Let's try to expand to 14 daysinstead.
@@ -155,33 +159,41 @@ class SimpleHistoryFilterDropin {
155
  placeholder="<?php echo _e("All dates", "simple-history") ?>" multiple>
156
  <?php
157
 
158
- // Last week + two weeks back + 30 days back
 
 
 
 
 
 
 
 
159
  printf(
160
  '<option value="%1$s" %3$s>%2$s</option>',
161
  "lastdays:7", // 1 - value
162
  _x("Last 7 days", "Filter dropin: filter week", "simple-history"), // 2 text
163
- selected($daysToShow, 7, 0)
164
  );
165
 
166
  printf(
167
  '<option value="%1$s" %3$s>%2$s</option>',
168
  "lastdays:14", // 1 - value
169
  _x("Last 14 days", "Filter dropin: filter week", "simple-history"), // 2 text
170
- selected($daysToShow, 14, 0)
171
  );
172
 
173
  printf(
174
  '<option value="%1$s" %3$s>%2$s</option>',
175
  "lastdays:30", // 1 - value
176
  _x("Last 30 days", "Filter dropin: filter week", "simple-history"), // 2 text
177
- selected($daysToShow, 30, 0)
178
  );
179
 
180
  printf(
181
  '<option value="%1$s" %3$s>%2$s</option>',
182
  "lastdays:60", // 1 - value
183
  _x("Last 60 days", "Filter dropin: filter week", "simple-history"), // 2 text
184
- selected($daysToShow, 60, 0)
185
  );
186
 
187
  // Months
127
  $numEvents = $this->get_unique_events_for_days($daysToShow);
128
  $numPages = $numEvents / $this->sh->get_pager_size();
129
 
130
+ // Example on my server with lots of brute force attacks
131
+ // 166434 / 15 = 11 000 pages for last 7 days
132
+ // 1 day = 3051 / 15 = 203 pages = still much but better than 11000 pages!
133
+
134
  if ( $numPages < 20 ) {
135
 
136
  // Not that many things the last 7 days. Let's try to expand to 14 daysinstead.
159
  placeholder="<?php echo _e("All dates", "simple-history") ?>" multiple>
160
  <?php
161
 
162
+ // One day+ Last week + two weeks back + 30 days back
163
+
164
+ printf(
165
+ '<option value="%1$s" %3$s>%2$s</option>',
166
+ "lastdays:1", // 1 - value
167
+ _x("Last day", "Filter dropin: filter week", "simple-history"), // 2 text
168
+ selected( $daysToShow, 1, 0 )
169
+ );
170
+
171
  printf(
172
  '<option value="%1$s" %3$s>%2$s</option>',
173
  "lastdays:7", // 1 - value
174
  _x("Last 7 days", "Filter dropin: filter week", "simple-history"), // 2 text
175
+ selected( $daysToShow, 7, 0 )
176
  );
177
 
178
  printf(
179
  '<option value="%1$s" %3$s>%2$s</option>',
180
  "lastdays:14", // 1 - value
181
  _x("Last 14 days", "Filter dropin: filter week", "simple-history"), // 2 text
182
+ selected( $daysToShow, 14, 0 )
183
  );
184
 
185
  printf(
186
  '<option value="%1$s" %3$s>%2$s</option>',
187
  "lastdays:30", // 1 - value
188
  _x("Last 30 days", "Filter dropin: filter week", "simple-history"), // 2 text
189
+ selected( $daysToShow, 30, 0 )
190
  );
191
 
192
  printf(
193
  '<option value="%1$s" %3$s>%2$s</option>',
194
  "lastdays:60", // 1 - value
195
  _x("Last 60 days", "Filter dropin: filter week", "simple-history"), // 2 text
196
+ selected( $daysToShow, 60, 0 )
197
  );
198
 
199
  // Months
dropins/SimpleHistoryPluginPatchesDropin.php CHANGED
@@ -1,5 +1,4 @@
1
  <?php
2
-
3
  defined( 'ABSPATH' ) or die();
4
 
5
  /*
@@ -39,37 +38,55 @@ class SimpleHistoryPluginPatchesDropin {
39
  */
40
  function patch_captcha_on_login() {
41
 
42
- add_action( "simple_history/log/do_log", array( $this, "patch_captcha_on_login_on_log" ), 10, 3 );
43
 
44
  }
45
 
46
  // Detect that this log message is being called from Captha on login
47
- function patch_captcha_on_login_on_log( $level = null, $message = null, $context = null ) {
 
48
 
49
  if ( empty( $context ) || ! isset( $context["_message_key"] ) || "user_logged_out" != $context["_message_key"] ) {
50
  // Message key did not exist or was not "user_logged_out"
51
- return;
52
  }
53
-
 
 
54
  // codiga is the input with the captcha
 
55
  if ( ! isset( $_POST["log"], $_POST["pwd"], $_POST["wp-submit"], $_POST["codigo"] ) ) {
56
  // All needed post variables was not set
57
- return;
58
  }
 
59
 
60
  // The Captcha on login uses a class called 'Anderson_Makiyama_Captcha_On_Login'
61
- // and also a globla variable called $global $anderson_makiyama
62
  global $anderson_makiyama;
63
  if ( ! class_exists("Anderson_Makiyama_Captcha_On_Login") || ! isset( $anderson_makiyama ) ) {
64
- return;
65
  }
66
-
67
  // We must come from wp-login
 
 
68
  $wp_referer = wp_get_referer();
69
  if ( ! $wp_referer || ! "wp-login.php" == basename( $wp_referer ) ) {
70
- return;
71
  }
72
-
 
 
 
 
 
 
 
 
 
 
 
73
  $anderson_makiyama_indice = Anderson_Makiyama_Captcha_On_Login::PLUGIN_ID;
74
  $capcha_on_login_class_name = $anderson_makiyama[$anderson_makiyama_indice]::CLASS_NAME;
75
 
@@ -90,8 +107,9 @@ class SimpleHistoryPluginPatchesDropin {
90
 
91
  // Get the user logger
92
  $userLogger = $this->sh->getInstantiatedLoggerBySlug( "SimpleUserLogger" );
 
93
  if ( ! $userLogger ) {
94
- return;
95
  }
96
 
97
  // $userLogger->warningMessage("user_unknown_login_failed", $context);
@@ -119,7 +137,10 @@ class SimpleHistoryPluginPatchesDropin {
119
  // Get user id and email and login
120
  // Not passed to filter, but we have it in $_POST
121
  $login_username = isset( $_POST["log"] ) ? $_POST["log"] : null;
122
- if ($login_username ) {
 
 
 
123
 
124
  $user = get_user_by( "login", $login_username );
125
 
@@ -127,7 +148,6 @@ class SimpleHistoryPluginPatchesDropin {
127
 
128
  $context["login_user_id"] = $user->ID;
129
  $context["login_user_email"] = $user->user_email;
130
- $context["login_user_login"] = $user->user_login;
131
 
132
  }
133
 
@@ -136,17 +156,10 @@ class SimpleHistoryPluginPatchesDropin {
136
  $userLogger->warningMessage("user_login_failed", $context);
137
 
138
  // Cancel original log event
139
- return false;
140
-
141
- /*$this->system_debug_log(
142
- __FUNCTION__,
143
- $level,
144
- $message,
145
- $context,
146
- $last_login_status
147
- );
148
- */
149
 
 
 
150
  }
151
 
152
  /**
1
  <?php
 
2
  defined( 'ABSPATH' ) or die();
3
 
4
  /*
38
  */
39
  function patch_captcha_on_login() {
40
 
41
+ add_action( "simple_history/log/do_log", array( $this, "patch_captcha_on_login_on_log" ), 10, 5 );
42
 
43
  }
44
 
45
  // Detect that this log message is being called from Captha on login
46
+ // and that the message is "user_logged_out"
47
+ function patch_captcha_on_login_on_log( $doLog, $level = null, $message = null, $context = null, $loggerInstance = null ) {
48
 
49
  if ( empty( $context ) || ! isset( $context["_message_key"] ) || "user_logged_out" != $context["_message_key"] ) {
50
  // Message key did not exist or was not "user_logged_out"
51
+ return $doLog;
52
  }
53
+
54
+ // 22 nov 2015: disabled this check beacuse for example robots/scripts don't pass all args
55
+ // instead they only post "log" and "pwd"
56
  // codiga is the input with the captcha
57
+ /*
58
  if ( ! isset( $_POST["log"], $_POST["pwd"], $_POST["wp-submit"], $_POST["codigo"] ) ) {
59
  // All needed post variables was not set
60
+ return $doLog;
61
  }
62
+ */
63
 
64
  // The Captcha on login uses a class called 'Anderson_Makiyama_Captcha_On_Login'
65
+ // and also a global variable called $global $anderson_makiyama
66
  global $anderson_makiyama;
67
  if ( ! class_exists("Anderson_Makiyama_Captcha_On_Login") || ! isset( $anderson_makiyama ) ) {
68
+ return $doLog;
69
  }
70
+
71
  // We must come from wp-login
72
+ // Disabled 22 nov 2015 because robots/scripts dont send referer
73
+ /*
74
  $wp_referer = wp_get_referer();
75
  if ( ! $wp_referer || ! "wp-login.php" == basename( $wp_referer ) ) {
76
+ return $doLog;
77
  }
78
+ */
79
+
80
+ if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
81
+ return $doLog;
82
+ }
83
+
84
+ // File must be wp-login.php (can it even be another?)
85
+ $request_uri = basename( wp_unslash( $_SERVER['REQUEST_URI'] ) );
86
+ if ( "wp-login.php" !== $request_uri ) {
87
+ return $doLog;
88
+ }
89
+
90
  $anderson_makiyama_indice = Anderson_Makiyama_Captcha_On_Login::PLUGIN_ID;
91
  $capcha_on_login_class_name = $anderson_makiyama[$anderson_makiyama_indice]::CLASS_NAME;
92
 
107
 
108
  // Get the user logger
109
  $userLogger = $this->sh->getInstantiatedLoggerBySlug( "SimpleUserLogger" );
110
+
111
  if ( ! $userLogger ) {
112
+ return $doLog;
113
  }
114
 
115
  // $userLogger->warningMessage("user_unknown_login_failed", $context);
137
  // Get user id and email and login
138
  // Not passed to filter, but we have it in $_POST
139
  $login_username = isset( $_POST["log"] ) ? $_POST["log"] : null;
140
+
141
+ if ( $login_username ) {
142
+
143
+ $context["login_user_login"] = $login_username;
144
 
145
  $user = get_user_by( "login", $login_username );
146
 
148
 
149
  $context["login_user_id"] = $user->ID;
150
  $context["login_user_email"] = $user->user_email;
 
151
 
152
  }
153
 
156
  $userLogger->warningMessage("user_login_failed", $context);
157
 
158
  // Cancel original log event
159
+ $doLog = false;
 
 
 
 
 
 
 
 
 
160
 
161
+ return $doLog;
162
+
163
  }
164
 
165
  /**
dropins/SimpleHistorySidebarDropin.php CHANGED
@@ -97,18 +97,27 @@ class SimpleHistorySidebarDropin {
97
  */
98
 
99
  // Box about possible events missing
100
- $boxMissingEvents = '
101
  <div class="postbox">
102
- <h3 class="hndle">Missing events?</h3>
103
  <div class="inside">
104
- <p>Do you think things are missing in the log? Let me know about it.</p>
 
105
  </div>
106
  </div>
107
- ';
 
 
 
 
 
 
 
108
  //echo $boxMissingEvents;
109
 
110
  $arrBoxes = array(
111
  "boxReview" => $boxReview,
 
112
  "boxDonate" => $boxDonate,
113
  // "boxGithub" => $boxGithub,
114
  );
97
  */
98
 
99
  // Box about possible events missing
100
+ $boxMissingEvents = sprintf( '
101
  <div class="postbox">
102
+ <h3 class="hndle">%1$s</h3>
103
  <div class="inside">
104
+ <p>%2$s</p>
105
+ <p><a href="hello@simple-history.com">hello@simple-history.com</a></p>
106
  </div>
107
  </div>
108
+ ',
109
+ _x("Add more to the log", "Sidebar box", "simple-history"), // 1
110
+ _x("Are there things you miss in the history log?", "Sidebar box", "simple-history") // 2
111
+
112
+ // Add events yourself using the Logger API
113
+ // Tell the developer
114
+
115
+ );
116
  //echo $boxMissingEvents;
117
 
118
  $arrBoxes = array(
119
  "boxReview" => $boxReview,
120
+ "boxMissingEvents" => $boxMissingEvents,
121
  "boxDonate" => $boxDonate,
122
  // "boxGithub" => $boxGithub,
123
  );
examples/example-dropin.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // No external calls allowed to test file
4
+ exit;
5
+
6
+
7
+ /**
8
+ * This example shows how to create a simple dropin
9
+ * that will add a tab to the simple history settings page
10
+ */
11
+
12
+ // We use the function "register_logger" to tell tell SimpleHistory that our custom logger exists.
13
+ // We call it from inside the filter "simple_history/add_custom_logger".
14
+ add_action("simple_history/add_custom_dropin", function( $simpleHistory ) {
15
+
16
+ $simpleHistory->register_dropin("AddSettingsPageTab");
17
+
18
+ });
19
+
20
+
21
+ /**
22
+ * This is the class that does the main work!
23
+ */
24
+ class AddSettingsPageTab {
25
+
26
+ // This will hold a reference to the simple history instance
27
+ private $sh;
28
+
29
+ // simple history will pass itself to the constructor
30
+ function __construct( $sh ) {
31
+
32
+ $this->sh = $sh;
33
+
34
+ $this->init();
35
+
36
+ }
37
+
38
+ function init() {
39
+
40
+ add_action( "init", array( $this, "add_settings_tab" ) );
41
+
42
+ }
43
+
44
+ function add_settings_tab() {
45
+
46
+ $this->sh->registerSettingsTab( array(
47
+ "slug" => "my_unique_settings_tab_slug",
48
+ "name" => __( "Example tab", "simple-history" ),
49
+ "function" => array( $this, "settings_tab_output" ),
50
+ ) );
51
+
52
+ }
53
+
54
+ function settings_tab_output() {
55
+
56
+ ?>
57
+
58
+ <h3>Hi there!</h3>
59
+
60
+ <p>I'm the output from on settings tab.</p>
61
+
62
+ <?php
63
+
64
+ }
65
+
66
+ } // end class
67
+
68
+
inc/SimpleHistory.php CHANGED
@@ -19,6 +19,11 @@ class SimpleHistory {
19
  */
20
  private $externalLoggers;
21
 
 
 
 
 
 
22
  /**
23
  * Array with all instantiated loggers
24
  */
@@ -530,6 +535,7 @@ class SimpleHistory {
530
  public function setup_variables() {
531
 
532
  $this->externalLoggers = array();
 
533
  $this->instantiatedLoggers = array();
534
  $this->instantiatedDropins = array();
535
 
@@ -609,7 +615,7 @@ class SimpleHistory {
609
 
610
  /**
611
  * Register an external logger so Simple History knows about it.
612
- * Does not load the logger, so file with logger must be loaded already.
613
  *
614
  * See example-logger.php for an example on how to use this.
615
  *
@@ -621,6 +627,20 @@ class SimpleHistory {
621
 
622
  }
623
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
624
  /**
625
  * Load built in loggers from all files in /loggers
626
  * and instantiates them
@@ -696,14 +716,13 @@ class SimpleHistory {
696
  }
697
 
698
  /**
699
- * Action that plugins should use to add their custom loggers.
700
  * See register_logger() for more info.
701
  *
702
  * @since 2.1
703
  *
704
- * @param array $arrLoggersToInstantiate Array with class names
705
  */
706
-
707
  do_action( "simple_history/add_custom_logger", $this );
708
 
709
  $arrLoggersToInstantiate = array_merge( $arrLoggersToInstantiate, $this->externalLoggers );
@@ -890,6 +909,16 @@ class SimpleHistory {
890
 
891
  }
892
 
 
 
 
 
 
 
 
 
 
 
893
  /**
894
  * Filter the array with names of dropin to instantiate.
895
  *
@@ -899,6 +928,8 @@ class SimpleHistory {
899
  */
900
  $arrDropinsToInstantiate = apply_filters( "simple_history/dropins_to_instantiate", $arrDropinsToInstantiate );
901
 
 
 
902
  // Instantiate each dropin
903
  foreach ( $arrDropinsToInstantiate as $oneDropinName ) {
904
 
@@ -3013,3 +3044,4 @@ function simple_history_text_diff( $left_string, $right_string, $args = null ) {
3013
 
3014
  return $r;
3015
  }
 
19
  */
20
  private $externalLoggers;
21
 
22
+ /**
23
+ * Array with external dropins to load
24
+ */
25
+ private $externalDropins;
26
+
27
  /**
28
  * Array with all instantiated loggers
29
  */
535
  public function setup_variables() {
536
 
537
  $this->externalLoggers = array();
538
+ $this->externalDropins = array();
539
  $this->instantiatedLoggers = array();
540
  $this->instantiatedDropins = array();
541
 
615
 
616
  /**
617
  * Register an external logger so Simple History knows about it.
618
+ * Does not load the logger, so file with logger class must be loaded already.
619
  *
620
  * See example-logger.php for an example on how to use this.
621
  *
627
 
628
  }
629
 
630
+ /**
631
+ * Register an external dropin so Simple History knows about it.
632
+ * Does not load the dropin, so file with dropin class must be loaded already.
633
+ *
634
+ * See example-dropin.php for an example on how to use this.
635
+ *
636
+ * @since 2.1
637
+ */
638
+ function register_dropin( $dropinClassName ) {
639
+
640
+ $this->externalDropins[] = $dropinClassName;
641
+
642
+ }
643
+
644
  /**
645
  * Load built in loggers from all files in /loggers
646
  * and instantiates them
716
  }
717
 
718
  /**
719
+ * Action that plugins can use to add their custom loggers.
720
  * See register_logger() for more info.
721
  *
722
  * @since 2.1
723
  *
724
+ * @param SimpleHistory instance
725
  */
 
726
  do_action( "simple_history/add_custom_logger", $this );
727
 
728
  $arrLoggersToInstantiate = array_merge( $arrLoggersToInstantiate, $this->externalLoggers );
909
 
910
  }
911
 
912
+ /**
913
+ * Action that dropins can use to add their custom loggers.
914
+ * See register_dropin() for more info.
915
+ *
916
+ * @since 2.3.2
917
+ *
918
+ * @param array $arrDropinsToInstantiate Array with class names
919
+ */
920
+ do_action( "simple_history/add_custom_dropin", $this );
921
+
922
  /**
923
  * Filter the array with names of dropin to instantiate.
924
  *
928
  */
929
  $arrDropinsToInstantiate = apply_filters( "simple_history/dropins_to_instantiate", $arrDropinsToInstantiate );
930
 
931
+ $arrDropinsToInstantiate = array_merge( $arrDropinsToInstantiate, $this->externalDropins );
932
+
933
  // Instantiate each dropin
934
  foreach ( $arrDropinsToInstantiate as $oneDropinName ) {
935
 
3044
 
3045
  return $r;
3046
  }
3047
+
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.3.1
9
  Author: Pär Thernström
10
  Author URI: http://simple-history.com/
11
  License: GPL2
@@ -34,10 +34,6 @@ if ( ! defined( 'WPINC' ) ) {
34
 
35
  if ( version_compare( phpversion(), "5.3", ">=") ) {
36
 
37
- /** Load required files */
38
- require_once(__DIR__ . "/inc/SimpleHistory.php");
39
- require_once(__DIR__ . "/inc/SimpleHistoryLogQuery.php");
40
-
41
  /**
42
  * Register function that is called when plugin is installed
43
  *
@@ -46,7 +42,7 @@ if ( version_compare( phpversion(), "5.3", ">=") ) {
46
  // register_activation_hook( trailingslashit(WP_PLUGIN_DIR) . trailingslashit( plugin_basename(__DIR__) ) . "index.php" , array("SimpleHistory", "on_plugin_activate" ) );
47
 
48
  if ( ! defined( 'SIMPLE_HISTORY_VERSION' ) ) {
49
- define( 'SIMPLE_HISTORY_VERSION', '2.3.1' );
50
  }
51
 
52
  if ( ! defined( 'SIMPLE_HISTORY_PATH' ) ) {
@@ -65,6 +61,10 @@ if ( version_compare( phpversion(), "5.3", ">=") ) {
65
  define( 'SIMPLE_HISTORY_FILE', __FILE__ );
66
  }
67
 
 
 
 
 
68
  // Prev behavior:
69
  /*
70
  define( 'SIMPLE_HISTORY_FILE', __FILE__ );
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.4
9
  Author: Pär Thernström
10
  Author URI: http://simple-history.com/
11
  License: GPL2
34
 
35
  if ( version_compare( phpversion(), "5.3", ">=") ) {
36
 
 
 
 
 
37
  /**
38
  * Register function that is called when plugin is installed
39
  *
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.4' );
46
  }
47
 
48
  if ( ! defined( 'SIMPLE_HISTORY_PATH' ) ) {
61
  define( 'SIMPLE_HISTORY_FILE', __FILE__ );
62
  }
63
 
64
+ /** Load required files */
65
+ require_once(__DIR__ . "/inc/SimpleHistory.php");
66
+ require_once(__DIR__ . "/inc/SimpleHistoryLogQuery.php");
67
+
68
  // Prev behavior:
69
  /*
70
  define( 'SIMPLE_HISTORY_FILE', __FILE__ );
loggers/SimpleLogger.php CHANGED
@@ -119,7 +119,7 @@ class SimpleLogger {
119
  * @param array $context
120
  * @param array $row Currently not always passed, because loggers need to be updated to support this...
121
  */
122
- function interpolate($message, $context = array(), $row = null) {
123
 
124
  if ( ! is_array( $context ) ) {
125
  return $message;
@@ -135,10 +135,28 @@ class SimpleLogger {
135
  // Build a replacement array with braces around the context keys
136
  $replace = array();
137
  foreach ( $context as $key => $val ) {
 
 
 
 
 
 
138
  $replace['{' . $key . '}'] = $val;
 
139
  }
140
 
141
  // Interpolate replacement values into the message and return
 
 
 
 
 
 
 
 
 
 
 
142
  return strtr($message, $replace);
143
 
144
  }
@@ -395,16 +413,25 @@ class SimpleLogger {
395
  $str_when = sprintf(__('%1$s ago', 'simple-history'), $date_human_time_diff);
396
 
397
  }
398
-
399
  $item_permalink = admin_url("index.php?page=simple_history_page");
400
  $item_permalink .= "#item/{$row->id}";
401
 
 
 
 
 
 
 
 
 
402
  $date_html = "<span class='SimpleHistoryLogitem__permalink SimpleHistoryLogitem__when SimpleHistoryLogitem__inlineDivided'>";
403
  $date_html .= "<a class='' href='{$item_permalink}'>";
404
  $date_html .= sprintf(
405
- '<time datetime="%1$s" title="%1$s" class="">%2$s</time>',
406
- $date_datetime->format(DateTime::RFC3339), // 1 datetime attribute
407
- $str_when
 
408
  );
409
  $date_html .= "</a>";
410
  $date_html .= "</span>";
@@ -872,7 +899,8 @@ class SimpleLogger {
872
  *
873
  * @since 2.3.1
874
  */
875
- $do_log = apply_filters( "simple_history/log/do_log", $level, $message, $context );
 
876
  if ( $do_log === false ) {
877
  return $this;
878
  }
119
  * @param array $context
120
  * @param array $row Currently not always passed, because loggers need to be updated to support this...
121
  */
122
+ function interpolate( $message, $context = array(), $row = null ) {
123
 
124
  if ( ! is_array( $context ) ) {
125
  return $message;
135
  // Build a replacement array with braces around the context keys
136
  $replace = array();
137
  foreach ( $context as $key => $val ) {
138
+
139
+ // Both key and val must be strings
140
+ if ( ! is_string( $key ) || ! is_string( $val ) ) {
141
+ continue;
142
+ }
143
+
144
  $replace['{' . $key . '}'] = $val;
145
+
146
  }
147
 
148
  // Interpolate replacement values into the message and return
149
+ /*
150
+ if ( ! is_string( $message )) {
151
+ echo "message:";
152
+ var_dump($message);exit;
153
+ }
154
+ if ( ! is_string( $replace )) {
155
+ echo "replace";
156
+ var_dump($replace);exit;
157
+ }
158
+ // */
159
+
160
  return strtr($message, $replace);
161
 
162
  }
413
  $str_when = sprintf(__('%1$s ago', 'simple-history'), $date_human_time_diff);
414
 
415
  }
416
+
417
  $item_permalink = admin_url("index.php?page=simple_history_page");
418
  $item_permalink .= "#item/{$row->id}";
419
 
420
+ $date_format = get_option('date_format') . ' - '. get_option('time_format');
421
+ $str_datetime_title = sprintf(
422
+ __('%1$s local time %3$s (%2$s GMT time)', "simple-history"),
423
+ get_date_from_gmt( $date_datetime->format('Y-m-d H:i:s'), $date_format ), // 1 local time
424
+ $date_datetime->format( $date_format ), // GMT time
425
+ PHP_EOL // 3, new line
426
+ );
427
+
428
  $date_html = "<span class='SimpleHistoryLogitem__permalink SimpleHistoryLogitem__when SimpleHistoryLogitem__inlineDivided'>";
429
  $date_html .= "<a class='' href='{$item_permalink}'>";
430
  $date_html .= sprintf(
431
+ '<time datetime="%3$s" title="%1$s" class="">%2$s</time>',
432
+ esc_attr( $str_datetime_title ), // 1 datetime attribute
433
+ esc_html( $str_when ), // 2 date text, visible in log
434
+ $date_datetime->format( DateTime::RFC3339 ) // 3
435
  );
436
  $date_html .= "</a>";
437
  $date_html .= "</span>";
899
  *
900
  * @since 2.3.1
901
  */
902
+ $do_log = apply_filters( "simple_history/log/do_log", true, $level, $message, $context, $this );
903
+
904
  if ( $do_log === false ) {
905
  return $this;
906
  }
loggers/SimpleUserLogger.php CHANGED
@@ -29,6 +29,8 @@ class SimpleUserLogger extends SimpleLogger {
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
 
33
  /*
34
  Text used in admin:
@@ -115,6 +117,98 @@ class SimpleUserLogger extends SimpleLogger {
115
  // User sessions is destroyed. AJAX call that we hook onto early.
116
  add_action("wp_ajax_destroy-sessions", array($this, "on_destroy_user_session"), 0);
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  }
119
 
120
  /**
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" => __("Reseted 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:
117
  // User sessions is destroyed. AJAX call that we hook onto early.
118
  add_action("wp_ajax_destroy-sessions", array($this, "on_destroy_user_session"), 0);
119
 
120
+ // User reaches reset password (from link or only from user created link)
121
+ add_action( 'validate_password_reset', array( $this, "on_validate_password_reset" ), 10, 2 );
122
+
123
+ add_action( 'retrieve_password_message', array( $this, "on_retrieve_password_message" ), 10, 4 );
124
+
125
+ }
126
+
127
+ /*
128
+
129
+ user requests a reset password link
130
+
131
+ $errors = apply_filters( 'wp_login_errors', $errors, $redirect_to );
132
+
133
+ elseif ( isset($_GET['checkemail']) && 'confirm' == $_GET['checkemail'] )
134
+ $errors->add('confirm', __('Check your e-mail for the confirmation link.'), 'message');
135
+
136
+ */
137
+ function on_retrieve_password_message( $message, $key, $user_login, $user_data ) {
138
+
139
+ if ( isset( $_GET["action"] ) && ( "lostpassword" == $_GET["action"] ) ) {
140
+
141
+ $context = array(
142
+ "_initiator" => SimpleLoggerLogInitiators::WEB_USER,
143
+ "message" => $message,
144
+ "key" => $key,
145
+ "user_login" => $user_login,
146
+ "user_data" => $user_data,
147
+ "GET" => $_GET,
148
+ "POST" => $_POST
149
+ );
150
+
151
+ if ( is_a($user_data, "WP_User") ) {
152
+
153
+ $context["user_email"] = $user_data->user_email;
154
+
155
+ }
156
+
157
+ $this->noticeMessage( "user_requested_password_reset_link", $context );
158
+
159
+ }
160
+
161
+ return $message;
162
+
163
+ }
164
+
165
+ /**
166
+ * Fired before the password reset procedure is validated.
167
+ *
168
+ * @param object $errors WP Error object.
169
+ * @param WP_User|WP_Error $user WP_User object if the login and reset key match. WP_Error object otherwise.
170
+ */
171
+ function on_validate_password_reset( $errors, $user ) {
172
+
173
+ /*
174
+ User visits the forgot password screen
175
+ $errors object are empty
176
+ $user contains a user
177
+ $_post is empty
178
+
179
+ User resets password
180
+ $errors empty
181
+ $user user object
182
+ $_post
183
+
184
+ */
185
+
186
+ $context = array();
187
+
188
+ if ( is_a( $user, "WP_User") ) {
189
+ $context["_initiator"] = SimpleLoggerLogInitiators::WP_USER;
190
+ $context["_user_id"] = $user->ID;
191
+ $context["_user_login"] = $user->user_login;
192
+ $context["_user_email"] = $user->user_email;
193
+ }
194
+
195
+ if ( isset($_POST['pass1']) && $_POST['pass1'] != $_POST['pass2'] ) {
196
+
197
+ // $errors->add( 'password_reset_mismatch', __( 'The passwords do not match.' ) );
198
+ // user failed to reset password
199
+
200
+ }
201
+
202
+
203
+ if ( ( ! $errors->get_error_code() ) && isset( $_POST['pass1'] ) && !empty( $_POST['pass1'] ) ) {
204
+
205
+ // login_header( __( 'Password Reset' ), '<p class="message reset-pass">' . __( 'Your password has been reset.' ) . ' <a href="' . esc_url(
206
+ $this->infoMessage( "user_password_reseted", $context );
207
+
208
+
209
+ }
210
+
211
+
212
  }
213
 
214
  /**
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://eskapism.se/sida/donate/
4
  Tags: history, log, changes, changelog, audit, trail, pages, attachments, users, cms, dashboard, admin, syslog, feed, activity, stream, audit trail, brute-force
5
  Requires at least: 3.6.0
6
  Tested up to: 4.3
7
- Stable tag: 2.3.1
8
 
9
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
10
 
@@ -125,22 +125,39 @@ https://github.com/bonny/WordPress-Simple-History
125
  are of type post and pages and media (i.e. images & other uploads), and only events
126
  initiated by a specific user.
127
 
128
- 2. Events with different severity Simple History uses the log levels specified in the PHP PSR-3 standard.
129
 
130
- 3. Events have context with extra details - Each logged event can include useful rich formatted extra information. For example: a plugin install can contain author info and a the url to the plugin, and an uploaded image can contain a thumbnail of the image.
131
 
132
- 4. Click on the IP address of an entry to view the location of for example a failed login attempt.
 
 
 
 
133
 
134
 
135
  == Changelog ==
136
 
137
  ## Changelog
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  = 2.3.1 (October 2015) =
140
 
141
  - Fixed: Hopefully fixed the wrong relative time, as reported here: https://wordpress.org/support/topic/wrong-reporting-time.
142
  - Changed: The RSS-feed with updates is now disabled by default for new installs. It is password protected, but some users felt that is should be optional to activate it. And now it is! Thanks to https://github.com/guillaumemolter for adding this feature.
143
- - Fixed: Failed login entries when using plugin (Captcha on Login)[https://wordpress.org/plugins/captcha-on-login/] was reported as "Logged out" when they really meant "Failed to log in". Please note that this was nothing that Simple History did wrong, it was rather Captcha on Login that manually called `wp_logout()` each time a user failed to login. Should fix all those mystery "Logged out"-entried some of you users had.
144
  - Added: Filter `simple_history/log/do_log` that can be used to shortcut the log()-method.
145
  - Updated: German translation updated.
146
 
4
  Tags: history, log, changes, changelog, audit, trail, pages, attachments, users, cms, dashboard, admin, syslog, feed, activity, stream, audit trail, brute-force
5
  Requires at least: 3.6.0
6
  Tested up to: 4.3
7
+ Stable tag: 2.4
8
 
9
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
10
 
125
  are of type post and pages and media (i.e. images & other uploads), and only events
126
  initiated by a specific user.
127
 
128
+ 2. The __Post Quick Diff__ feature will make it quick and easy for a user of a site to see what updates other users are have done to posts and pages.
129
 
130
+ 3. Events with different severity Simple History uses the log levels specified in the PHP PSR-3 standard.
131
 
132
+ 4. Events have context with extra details - Each logged event can include useful rich formatted extra information. For example: a plugin install can contain author info and a the url to the plugin, and an uploaded image can contain a thumbnail of the image.
133
+
134
+ 5. Click on the IP address of an entry to view the location of for example a failed login attempt.
135
+
136
+ 6. See even more details about a logged event (by clicking on the date and time of the event).
137
 
138
 
139
  == Changelog ==
140
 
141
  ## Changelog
142
 
143
+ = 2.4 (November 2015) =
144
+
145
+ - Added: Now logs when a user changes their password using the "reset password" link.
146
+ - Added: Now logs when a user uses the password reset form.
147
+ - Added: New method `register_dropin` that can be used to add dropins.
148
+ - Added: New action `simple_history/add_custom_dropin`.
149
+ - Added: Example on how to add an external dropin: [example-dropin.php](https://github.com/bonny/WordPress-Simple-History/blob/master/examples/example-dropin.php).
150
+ - Added: "Last day" added to filter, because a brute force attack can add so many logs that it's not possible to fetch a whole week.
151
+ - Changed: Filter `simple_history/log/do_log` now pass 5 arguments instead of 3. Before this update in was not possible for multiple add_action()-calls to use this filter, because you would not now if any other code had canceled it and so on. If you have been using this filter you need to modify your code.
152
+ - Changed: When hovering the time of an event in the log, the date of the event displays in both local time and GMT time. Hopefully makes it easier for admins in different timezones that work together on a site to understand when each event happened. Fixes https://github.com/bonny/WordPress-Simple-History/issues/84.
153
+ - Fixed: Line height was a bit tight on the dashboard. Also: the margin was a tad to small for the first logged event on the dashboard.
154
+ - Fixed: Username was not added correctly to failed login attempts when using plugin Captcha on Login + it would still show that a user logged out sometimes when a bot/script brute force attacked a site by only sending login and password and not the captcha field.
155
+
156
  = 2.3.1 (October 2015) =
157
 
158
  - Fixed: Hopefully fixed the wrong relative time, as reported here: https://wordpress.org/support/topic/wrong-reporting-time.
159
  - Changed: The RSS-feed with updates is now disabled by default for new installs. It is password protected, but some users felt that is should be optional to activate it. And now it is! Thanks to https://github.com/guillaumemolter for adding this feature.
160
+ - Fixed: Failed login entries when using plugin [Captcha on Login](https://wordpress.org/plugins/captcha-on-login/) was reported as "Logged out" when they really meant "Failed to log in". Please note that this was nothing that Simple History did wrong, it was rather Captcha on Login that manually called `wp_logout()` each time a user failed to login. Should fix all those mystery "Logged out"-entried some of you users had.
161
  - Added: Filter `simple_history/log/do_log` that can be used to shortcut the log()-method.
162
  - Updated: German translation updated.
163