Version Description
(May 2017) =
- Added WP-CLI command for Simple History. Now you can write
wp simple-history list
to see the latest entries from the history log. For nowlist
is the only available command. Let me know if you need more commands! - Added support for logging edits to theme files and plugin files. When a file is edited you will also get a quick diff on the changes, so you can see what CSS styles a client changed or what PHP changes they made in a plugin file.
- Removed the edit file logger from the plugin logger, because it did not always work (checked wrong wp path). Intead the new Theme and plugins logger mentioned above will take care of this.
Download this release
Release Info
Developer | eskapism |
Plugin | Simple History |
Version | 2.16 |
Comparing to | |
See all releases |
Code changes from version 2.15 to 2.16
- dropins/SimpleHistoryDonateDropin.php +11 -11
- dropins/SimpleHistoryWPCLIDropin.php +199 -0
- inc/SimpleHistory.php +2 -0
- index.php +2 -2
- loggers/FileEditsLogger.php +265 -0
- loggers/PluginUserSwitchingLogger.php +0 -1
- loggers/SimplePluginLogger.php +1 -71
- readme.txt +8 -1
dropins/SimpleHistoryDonateDropin.php
CHANGED
@@ -18,7 +18,7 @@ class SimpleHistoryDonateDropin {
|
|
18 |
private $sh;
|
19 |
|
20 |
function __construct($sh) {
|
21 |
-
|
22 |
$this->sh = $sh;
|
23 |
add_action( 'admin_menu', array($this, 'add_settings'), 50 );
|
24 |
add_action( 'plugin_row_meta', array($this, 'action_plugin_row_meta'), 10, 2);
|
@@ -30,7 +30,7 @@ class SimpleHistoryDonateDropin {
|
|
30 |
* Called from filter 'plugin_row_meta'
|
31 |
*/
|
32 |
function action_plugin_row_meta($links, $file) {
|
33 |
-
|
34 |
if ($file == $this->sh->plugin_basename) {
|
35 |
|
36 |
$links = array_merge(
|
@@ -39,7 +39,7 @@ class SimpleHistoryDonateDropin {
|
|
39 |
);
|
40 |
|
41 |
}
|
42 |
-
|
43 |
return $links;
|
44 |
|
45 |
}
|
@@ -49,9 +49,9 @@ class SimpleHistoryDonateDropin {
|
|
49 |
$settings_section_id = "simple_history_settings_section_donate";
|
50 |
|
51 |
add_settings_section(
|
52 |
-
$settings_section_id,
|
53 |
-
_x("Donate", "donate settings headline", "simple-history"), // No title __("General", "simple-history"),
|
54 |
-
array($this, "settings_section_output"),
|
55 |
SimpleHistory::SETTINGS_MENU_SLUG // same slug as for options menu page
|
56 |
);
|
57 |
|
@@ -69,17 +69,17 @@ class SimpleHistoryDonateDropin {
|
|
69 |
}
|
70 |
|
71 |
function settings_section_output() {
|
72 |
-
|
73 |
-
printf(
|
74 |
__( 'If you find Simple History useful please <a href="%1$s">donate</a>.', "simple-history"),
|
75 |
-
"http://eskapism.se/sida/donate/?utm_source=wordpress&utm_medium=pluginpage&utm_campaign=simplehistory",
|
76 |
"http://www.amazon.co.uk/registry/wishlist/IAEZWNLQQICG"
|
77 |
);
|
78 |
-
|
79 |
}
|
80 |
|
81 |
|
82 |
function settings_field_donate() {
|
83 |
}
|
84 |
|
85 |
-
}
|
18 |
private $sh;
|
19 |
|
20 |
function __construct($sh) {
|
21 |
+
|
22 |
$this->sh = $sh;
|
23 |
add_action( 'admin_menu', array($this, 'add_settings'), 50 );
|
24 |
add_action( 'plugin_row_meta', array($this, 'action_plugin_row_meta'), 10, 2);
|
30 |
* Called from filter 'plugin_row_meta'
|
31 |
*/
|
32 |
function action_plugin_row_meta($links, $file) {
|
33 |
+
|
34 |
if ($file == $this->sh->plugin_basename) {
|
35 |
|
36 |
$links = array_merge(
|
39 |
);
|
40 |
|
41 |
}
|
42 |
+
|
43 |
return $links;
|
44 |
|
45 |
}
|
49 |
$settings_section_id = "simple_history_settings_section_donate";
|
50 |
|
51 |
add_settings_section(
|
52 |
+
$settings_section_id,
|
53 |
+
_x("Donate", "donate settings headline", "simple-history"), // No title __("General", "simple-history"),
|
54 |
+
array($this, "settings_section_output"),
|
55 |
SimpleHistory::SETTINGS_MENU_SLUG // same slug as for options menu page
|
56 |
);
|
57 |
|
69 |
}
|
70 |
|
71 |
function settings_section_output() {
|
72 |
+
|
73 |
+
printf(
|
74 |
__( 'If you find Simple History useful please <a href="%1$s">donate</a>.', "simple-history"),
|
75 |
+
"http://eskapism.se/sida/donate/?utm_source=wordpress&utm_medium=pluginpage&utm_campaign=simplehistory",
|
76 |
"http://www.amazon.co.uk/registry/wishlist/IAEZWNLQQICG"
|
77 |
);
|
78 |
+
|
79 |
}
|
80 |
|
81 |
|
82 |
function settings_field_donate() {
|
83 |
}
|
84 |
|
85 |
+
}
|
dropins/SimpleHistoryWPCLIDropin.php
ADDED
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
defined( 'ABSPATH' ) or die();
|
4 |
+
|
5 |
+
/*
|
6 |
+
Dropin Name: WP CLI
|
7 |
+
Dropin URI: http://simple-history.com/
|
8 |
+
Author: Pär Thernström
|
9 |
+
*/
|
10 |
+
|
11 |
+
class SimpleHistoryWPCLIDropin {
|
12 |
+
|
13 |
+
// Simple History instance
|
14 |
+
private $sh;
|
15 |
+
|
16 |
+
function __construct($sh) {
|
17 |
+
|
18 |
+
$this->sh = $sh;
|
19 |
+
#add_action( 'admin_menu', array($this, 'add_settings'), 50 );
|
20 |
+
#add_action( 'plugin_row_meta', array($this, 'action_plugin_row_meta'), 10, 2);
|
21 |
+
|
22 |
+
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
23 |
+
$this->register_commands();
|
24 |
+
}
|
25 |
+
|
26 |
+
}
|
27 |
+
|
28 |
+
private function register_commands() {
|
29 |
+
$commandConfigurationOptions = array(
|
30 |
+
'shortdesc' => 'Lists the history log',
|
31 |
+
'synopsis' => array(
|
32 |
+
array(
|
33 |
+
'type' => 'assoc',
|
34 |
+
'name' => 'format',
|
35 |
+
'optional' => true,
|
36 |
+
'default' => 'table',
|
37 |
+
'options' => array( 'table', 'json', 'csv', 'yaml' ),
|
38 |
+
),
|
39 |
+
array(
|
40 |
+
'type' => 'assoc',
|
41 |
+
'name' => 'count',
|
42 |
+
'optional' => true,
|
43 |
+
'default' => '10',
|
44 |
+
),
|
45 |
+
),
|
46 |
+
'when' => 'after_wp_load',
|
47 |
+
);
|
48 |
+
|
49 |
+
WP_CLI::add_command( 'simple-history list', array($this, 'commandList'), $commandConfigurationOptions );
|
50 |
+
}
|
51 |
+
|
52 |
+
private function getInitiatorTextFromRow( $row ) {
|
53 |
+
if (! isset( $row->initiator )) {
|
54 |
+
return false;
|
55 |
+
}
|
56 |
+
|
57 |
+
$initiator = $row->initiator;
|
58 |
+
$initiatorText = '';
|
59 |
+
|
60 |
+
switch ($initiator) {
|
61 |
+
case "wp":
|
62 |
+
$initiatorText = 'WordPress';
|
63 |
+
break;
|
64 |
+
case "wp_cli":
|
65 |
+
$initiatorText = 'WP-CLI';
|
66 |
+
break;
|
67 |
+
case "wp_user":
|
68 |
+
$user_id = isset($row->context["_user_id"]) ? $row->context["_user_id"] : null;
|
69 |
+
|
70 |
+
if ( $user_id > 0 && $user = get_user_by("id", $user_id) ) {
|
71 |
+
// User still exists
|
72 |
+
|
73 |
+
// Get user role, as done in user-edit.php
|
74 |
+
$wp_roles = $GLOBALS["wp_roles"];
|
75 |
+
$all_roles = (array) $wp_roles->roles;
|
76 |
+
$user_roles = array_intersect( array_values( (array) $user->roles ), array_keys( (array) $wp_roles->roles ));
|
77 |
+
$user_role = array_shift( $user_roles );
|
78 |
+
|
79 |
+
$initiatorText = sprintf(
|
80 |
+
'%1$s (%2$s)' ,
|
81 |
+
$user->user_login, // 1
|
82 |
+
$user->user_email // 2
|
83 |
+
);
|
84 |
+
|
85 |
+
} else if ($user_id > 0) {
|
86 |
+
// Sender was a user, but user is deleted now
|
87 |
+
$initiatorText = sprintf(
|
88 |
+
__('Deleted user (had id %1$s, email %2$s, login %3$s)', "simple-history"),
|
89 |
+
$context["_user_id"], // 1
|
90 |
+
$context["_user_email"], // 2
|
91 |
+
$context["_user_login"] // 3
|
92 |
+
);
|
93 |
+
} // if user exists or not
|
94 |
+
break;
|
95 |
+
case "web_user":
|
96 |
+
$initiatorText = __("Anonymous web user", "simple-history");
|
97 |
+
break;
|
98 |
+
case "other":
|
99 |
+
$initiatorText = _x("Other", "Event header output, when initiator is unknown", "simple-history");
|
100 |
+
break;
|
101 |
+
default:
|
102 |
+
$initiatorText = $initiator;
|
103 |
+
}
|
104 |
+
|
105 |
+
return $initiatorText;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* The function for the command "list"
|
110 |
+
*/
|
111 |
+
public function commandList( $args, $assoc_args ) {
|
112 |
+
|
113 |
+
if ( ! is_numeric($assoc_args["count"]) ) {
|
114 |
+
WP_CLI::error( __('Error: parameter "count" must be a number', 'simple-history' ) );
|
115 |
+
}
|
116 |
+
|
117 |
+
// Override capability check: if you can run wp cli commands you can read all loggers
|
118 |
+
add_action( 'simple_history/loggers_user_can_read/can_read_single_logger', '__return_true', 10, 3);
|
119 |
+
|
120 |
+
// WP_CLI::log( sprintf( 'Showing %1$d events from Simple History', $assoc_args["count"] ) );
|
121 |
+
|
122 |
+
$query = new SimpleHistoryLogQuery();
|
123 |
+
|
124 |
+
$query_args = array(
|
125 |
+
"paged" => 1,
|
126 |
+
"posts_per_page" => $assoc_args["count"]
|
127 |
+
);
|
128 |
+
|
129 |
+
$events = $query->query( $query_args );
|
130 |
+
|
131 |
+
// A cleaned version of the events, formatted for wp cli table output
|
132 |
+
$eventsCleaned = array();
|
133 |
+
|
134 |
+
foreach ($events["log_rows"] as $row) {
|
135 |
+
$header_output = $this->sh->getLogRowHeaderOutput($row);
|
136 |
+
$text_output = $this->sh->getLogRowPlainTextOutput($row);
|
137 |
+
// $details_output = $this->sh->getLogRowDetailsOutput($row);
|
138 |
+
|
139 |
+
$header_output = strip_tags( html_entity_decode( $header_output, ENT_QUOTES, 'UTF-8') );
|
140 |
+
$header_output = trim(preg_replace('/\s\s+/', ' ', $header_output));
|
141 |
+
|
142 |
+
$text_output = strip_tags( html_entity_decode( $text_output, ENT_QUOTES, 'UTF-8') );
|
143 |
+
|
144 |
+
$eventsCleaned[] = array(
|
145 |
+
"date" => get_date_from_gmt( $row->date ),
|
146 |
+
// "initiator" => $row->initiator,
|
147 |
+
"initiator" => $this->getInitiatorTextFromRow( $row ),
|
148 |
+
"logger" => $row->logger,
|
149 |
+
"level" => $row->level,
|
150 |
+
"who_when" => $header_output,
|
151 |
+
"description" => $text_output,
|
152 |
+
"count" => $row->subsequentOccasions,
|
153 |
+
// "details" => $details_output
|
154 |
+
);
|
155 |
+
}
|
156 |
+
|
157 |
+
#print_r($events);
|
158 |
+
#print_r($eventsCleaned);
|
159 |
+
/*
|
160 |
+
[9] => stdClass Object
|
161 |
+
(
|
162 |
+
[id] => 735
|
163 |
+
[logger] => AvailableUpdatesLogger
|
164 |
+
[level] => notice
|
165 |
+
[date] => 2017-05-19 12:45:13
|
166 |
+
[message] => Found an update to plugin "{plugin_name}"
|
167 |
+
[initiator] => wp
|
168 |
+
[occasionsID] => 9a2d42eebea5c3cd2b16db0c38258016
|
169 |
+
[subsequentOccasions] => 1
|
170 |
+
[rep] => 1
|
171 |
+
[repeated] => 10
|
172 |
+
[occasionsIDType] => 9a2d42eebea5c3cd2b16db0c38258016
|
173 |
+
[context_message_key] => plugin_update_available
|
174 |
+
[context] => Array
|
175 |
+
(
|
176 |
+
[plugin_name] => WooCommerce
|
177 |
+
[plugin_current_version] => 3.0.6
|
178 |
+
[plugin_new_version] => 3.0.7
|
179 |
+
[_message_key] => plugin_update_available
|
180 |
+
[_server_remote_addr] => ::1
|
181 |
+
[_server_http_referer] => http://wp-playground.dev/wp/wp-cron.php?doing_wp_cron=1495197902.1593680381774902343750
|
182 |
+
)
|
183 |
+
|
184 |
+
)
|
185 |
+
*/
|
186 |
+
|
187 |
+
$fields = array(
|
188 |
+
'date',
|
189 |
+
'initiator',
|
190 |
+
'description',
|
191 |
+
'level',
|
192 |
+
'count',
|
193 |
+
);
|
194 |
+
|
195 |
+
WP_CLI\Utils\format_items( $assoc_args['format'], $eventsCleaned, $fields );
|
196 |
+
|
197 |
+
}
|
198 |
+
|
199 |
+
}
|
inc/SimpleHistory.php
CHANGED
@@ -881,6 +881,7 @@ class SimpleHistory {
|
|
881 |
$loggersDir . "SimpleUserLogger.php",
|
882 |
$loggersDir . "SimpleCategoriesLogger.php",
|
883 |
$loggersDir . "AvailableUpdatesLogger.php",
|
|
|
884 |
|
885 |
// Loggers for third party plugins
|
886 |
$loggersDir . "PluginUserSwitchingLogger.php",
|
@@ -1080,6 +1081,7 @@ class SimpleHistory {
|
|
1080 |
$dropinsDir . "SimpleHistorySettingsDebugDropin.php",
|
1081 |
$dropinsDir . "SimpleHistorySidebarDropin.php",
|
1082 |
$dropinsDir . "SimpleHistorySidebarStats.php",
|
|
|
1083 |
);
|
1084 |
|
1085 |
/**
|
881 |
$loggersDir . "SimpleUserLogger.php",
|
882 |
$loggersDir . "SimpleCategoriesLogger.php",
|
883 |
$loggersDir . "AvailableUpdatesLogger.php",
|
884 |
+
$loggersDir . "FileEditsLogger.php",
|
885 |
|
886 |
// Loggers for third party plugins
|
887 |
$loggersDir . "PluginUserSwitchingLogger.php",
|
1081 |
$dropinsDir . "SimpleHistorySettingsDebugDropin.php",
|
1082 |
$dropinsDir . "SimpleHistorySidebarDropin.php",
|
1083 |
$dropinsDir . "SimpleHistorySidebarStats.php",
|
1084 |
+
$dropinsDir . "SimpleHistoryWPCLIDropin.php",
|
1085 |
);
|
1086 |
|
1087 |
/**
|
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.
|
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.
|
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.16
|
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.16' );
|
46 |
}
|
47 |
|
48 |
if ( ! defined( 'SIMPLE_HISTORY_PATH' ) ) {
|
loggers/FileEditsLogger.php
ADDED
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Logs edits to theme or plugin files done from Appearance -> Editor or Plugins -> Editor
|
5 |
+
*/
|
6 |
+
class FileEditsLogger extends SimpleLogger {
|
7 |
+
|
8 |
+
public $slug = __CLASS__;
|
9 |
+
|
10 |
+
function getInfo() {
|
11 |
+
|
12 |
+
$arr_info = array(
|
13 |
+
"name" => "FileEditsLogger",
|
14 |
+
"description" => "Logs edits to theme and plugin files",
|
15 |
+
"capability" => "manage_options",
|
16 |
+
"messages" => array(
|
17 |
+
"theme_file_edited" => __( 'Edited file "{file_name}" in theme "{theme_name}"', "simple-history" ),
|
18 |
+
"plugin_file_edited" => __( 'Edited file "{file_name}" in plugin "{plugin_name}"', "simple-history" ),
|
19 |
+
),
|
20 |
+
"labels" => array(
|
21 |
+
"search" => array(
|
22 |
+
"label" => _x("Edited theme and plugin files", "Plugin logger: file edits", "simple-history"),
|
23 |
+
"label_all" => _x("All file edits", "Plugin logger: file edits", "simple-history"),
|
24 |
+
"options" => array(
|
25 |
+
_x("Edited theme files", "Plugin logger: file edits", "simple-history") => array(
|
26 |
+
'theme_file_edited'
|
27 |
+
),
|
28 |
+
_x("Edited plugin files", "Plugin logger: file edits", "simple-history") => array(
|
29 |
+
'plugin_file_edited',
|
30 |
+
),
|
31 |
+
)
|
32 |
+
) // search array
|
33 |
+
) // labels
|
34 |
+
);
|
35 |
+
|
36 |
+
return $arr_info;
|
37 |
+
|
38 |
+
}
|
39 |
+
|
40 |
+
function loaded() {
|
41 |
+
add_action( 'load-theme-editor.php', array( $this, "on_load_theme_editor" ), 10, 1 );
|
42 |
+
add_action( 'load-plugin-editor.php', array( $this, "on_load_plugin_editor" ), 10, 1 );
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Called when /wp/wp-admin/plugin-editor.php is loaded
|
47 |
+
* Both using regular GET and during POST with updated file data
|
48 |
+
*
|
49 |
+
* todo:
|
50 |
+
* - log edits
|
51 |
+
* - log failed edits that result in error and plugin deactivation
|
52 |
+
*/
|
53 |
+
public function on_load_plugin_editor() {
|
54 |
+
if ( isset($_POST) && isset($_POST['action']) ) {
|
55 |
+
|
56 |
+
$action = isset($_POST["action"]) ? $_POST["action"] : null;
|
57 |
+
$file = isset($_POST["file"]) ? $_POST["file"] : null;
|
58 |
+
$plugin_file = isset($_POST["plugin"]) ? $_POST["plugin"] : null;
|
59 |
+
$fileNewContents = isset($_POST["newcontent"]) ? wp_unslash( $_POST["newcontent"] ) : null;
|
60 |
+
$scrollto = isset($_POST["scrollto"]) ? (int) $_POST["scrollto"] : 0;
|
61 |
+
|
62 |
+
// if 'phperror' is set then there was an error and an edit is done and wp tries to activate the plugin again
|
63 |
+
// $phperror = isset($_POST["phperror"]) ? $_POST["phperror"] : null;
|
64 |
+
|
65 |
+
// Get info about the edited plugin
|
66 |
+
$pluginInfo = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file );
|
67 |
+
$pluginName = isset( $pluginInfo['Name'] ) ? $pluginInfo['Name'] : null;
|
68 |
+
$pluginVersion = isset( $pluginInfo['Version'] ) ? $pluginInfo['Version'] : null;
|
69 |
+
|
70 |
+
// Get contents before save
|
71 |
+
$fileContentsBeforeEdit = file_get_contents( WP_PLUGIN_DIR . '/' . $file );
|
72 |
+
|
73 |
+
$context = array(
|
74 |
+
"file_name" => $plugin_file,
|
75 |
+
"plugin_name" => $pluginName,
|
76 |
+
"plugin_version" => $pluginVersion,
|
77 |
+
"old_file_contents" => $fileContentsBeforeEdit,
|
78 |
+
"new_file_contents" => $fileNewContents,
|
79 |
+
"_occasionsID" => __CLASS__ . '/' . __FUNCTION__ . "/file-edit/$plugin_file/$file"
|
80 |
+
);
|
81 |
+
|
82 |
+
#ddd($_POST, $context, $action, $file, $plugin, $phperror, $fileNewContents, $scrollto);
|
83 |
+
$loggerInstance = $this;
|
84 |
+
add_filter( 'wp_redirect', function ( $location, $status ) use ( $context, $loggerInstance ) {
|
85 |
+
error_log($location);
|
86 |
+
|
87 |
+
$locationParsed = parse_url( $location );
|
88 |
+
|
89 |
+
if ( $locationParsed === false || empty( $locationParsed['query'] ) ) {
|
90 |
+
return $location;
|
91 |
+
}
|
92 |
+
|
93 |
+
parse_str( $locationParsed['query'], $queryStringParsed);
|
94 |
+
#ddd($_POST, $context, $queryStringParsed, $location);
|
95 |
+
|
96 |
+
if (empty($queryStringParsed)) {
|
97 |
+
return $location;
|
98 |
+
}
|
99 |
+
|
100 |
+
// If query string "a=te" exists or "liveupdate=1" then plugin file was updated
|
101 |
+
$teIsSet = isset($queryStringParsed['a']) && $queryStringParsed['a'] === 'te';
|
102 |
+
$liveUpdateIsSet = isset($queryStringParsed['liveupdate']) && $queryStringParsed['liveupdate'] === '1';
|
103 |
+
if ( $teIsSet || $liveUpdateIsSet ) {
|
104 |
+
// File was updated
|
105 |
+
$loggerInstance->infoMessage('plugin_file_edited', $context);
|
106 |
+
}
|
107 |
+
|
108 |
+
return $location;
|
109 |
+
|
110 |
+
// location when successful edit to non-active plugin
|
111 |
+
// http://wp-playground.dev/wp/wp-admin/plugin-editor.php?file=akismet/akismet.php&plugin=akismet/akismet.php&a=te&scrollto=0
|
112 |
+
|
113 |
+
// locations when activated plugin edited successfully
|
114 |
+
// plugin-editor.php?file=akismet%2Fakismet.php&plugin=akismet%2Fakismet.php&liveupdate=1&scrollto=0&networkwide&_wpnonce=b3f399fe94
|
115 |
+
// plugin-editor.php?file=akismet%2Fakismet.php&phperror=1&_error_nonce=63511c266d
|
116 |
+
// http://wp-playground.dev/wp/wp-admin/plugin-editor.php?file=akismet/akismet.php&plugin=akismet/akismet.php&a=te&scrollto=0
|
117 |
+
|
118 |
+
// locations when editing active plugin and error occurs
|
119 |
+
// plugin-editor.php?file=akismet%2Fakismet.php&plugin=akismet%2Fakismet.php&liveupdate=1&scrollto=0&networkwide&_wpnonce=b3f399fe94
|
120 |
+
// plugin-editor.php?file=akismet%2Fakismet.php&phperror=1&_error_nonce=63511c266d
|
121 |
+
|
122 |
+
// locations when error edit is fixed and saved and plugin is activated again
|
123 |
+
// plugin-editor.php?file=akismet%2Fakismet.php&plugin=akismet%2Fakismet.php&liveupdate=1&scrollto=0&networkwide&_wpnonce=b3f399fe94
|
124 |
+
// plugin-editor.php?file=akismet%2Fakismet.php&phperror=1&_error_nonce=63511c266d
|
125 |
+
// http://wp-playground.dev/wp/wp-admin/plugin-editor.php?file=akismet/akismet.php&plugin=akismet/akismet.php&a=te&scrollto=0
|
126 |
+
|
127 |
+
}, 10, 2 );
|
128 |
+
|
129 |
+
}
|
130 |
+
/*
|
131 |
+
<?php if (isset($_GET['a'])) : ?>
|
132 |
+
<div id="message" class="updated notice is-dismissible"><p><?php _e('File edited successfully.') ?></p></div>
|
133 |
+
<?php elseif (isset($_GET['phperror'])) : ?>
|
134 |
+
<div id="message" class="updated"><p><?php _e('This plugin has been deactivated because your changes resulted in a <strong>fatal error</strong>.') ?></p>
|
135 |
+
*/
|
136 |
+
}
|
137 |
+
|
138 |
+
/**
|
139 |
+
* Called when /wp/wp-admin/theme-editor.php is loaded
|
140 |
+
* Both using regular GET and during POST with updated file data
|
141 |
+
*
|
142 |
+
* When this action is fired we don't know if a file will be successfully saved or not.
|
143 |
+
* There are no filters/actions fired when the edit is saved. On the end wp_redirect() is
|
144 |
+
* called however and we know the location for the redirect and wp_redirect() has filters
|
145 |
+
* so we hook onto that to save the edit.
|
146 |
+
*/
|
147 |
+
public function on_load_theme_editor() {
|
148 |
+
// Only continue if method is post and action is update
|
149 |
+
if (isset($_POST) && isset($_POST["action"]) && $_POST["action"] === "update") {
|
150 |
+
/*
|
151 |
+
POST data is like
|
152 |
+
array(8)
|
153 |
+
'_wpnonce' => string(10) "9b5e46634f"
|
154 |
+
'_wp_http_referer' => string(88) "/wp/wp-admin/theme-editor.php?file=style.css&theme=twentyfifteen&scrollto=0&upda…"
|
155 |
+
'newcontent' => string(104366) "/* Theme Name: Twenty Fifteen Theme URI: https://wordpress.org/themes/twentyfift…"
|
156 |
+
'action' => string(6) "update"
|
157 |
+
'file' => string(9) "style.css"
|
158 |
+
'theme' => string(13) "twentyfifteen"
|
159 |
+
'scrollto' => string(3) "638"
|
160 |
+
'submit' => string(11) "Update File"
|
161 |
+
*/
|
162 |
+
|
163 |
+
$action = isset($_POST["action"]) ? $_POST["action"] : null;
|
164 |
+
$file = isset($_POST["file"]) ? $_POST["file"] : null;
|
165 |
+
$theme = isset($_POST["theme"]) ? $_POST["theme"] : null;
|
166 |
+
$fileNewContents = isset($_POST["newcontent"]) ? wp_unslash( $_POST["newcontent"] ) : null;
|
167 |
+
$scrollto = isset($_POST["scrollto"]) ? (int) $_POST["scrollto"] : 0;
|
168 |
+
|
169 |
+
// Same code as in theme-editor.php
|
170 |
+
if ( $theme ) {
|
171 |
+
$stylesheet = $theme;
|
172 |
+
} else {
|
173 |
+
$stylesheet = get_stylesheet();
|
174 |
+
}
|
175 |
+
|
176 |
+
$theme = wp_get_theme( $stylesheet );
|
177 |
+
|
178 |
+
if (! is_a($theme, 'WP_Theme')) {
|
179 |
+
return;
|
180 |
+
}
|
181 |
+
|
182 |
+
// Same code as in theme-editor.php
|
183 |
+
$relative_file = $file;
|
184 |
+
$file = $theme->get_stylesheet_directory() . '/' . $relative_file;
|
185 |
+
|
186 |
+
// Get file contents, so we have something to compare with later
|
187 |
+
$fileContentsBeforeEdit = file_get_contents($file);
|
188 |
+
|
189 |
+
$context = array(
|
190 |
+
"theme_name" => $theme->name,
|
191 |
+
"theme_stylesheet_path" => $theme->get_stylesheet(),
|
192 |
+
"theme_stylesheet_dir" => $theme->get_stylesheet_directory(),
|
193 |
+
"file_name" => $relative_file,
|
194 |
+
"file_dir" => $file,
|
195 |
+
"old_file_contents" => $fileContentsBeforeEdit,
|
196 |
+
"new_file_contents" => $fileNewContents,
|
197 |
+
"_occasionsID" => __CLASS__ . '/' . __FUNCTION__ . "/file-edit/$file"
|
198 |
+
);
|
199 |
+
|
200 |
+
// Hook into wp_redirect
|
201 |
+
// This hook is only added when we know a POST is done from theme-editor.php
|
202 |
+
$loggerInstance = $this;
|
203 |
+
add_filter( 'wp_redirect', function ( $location, $status ) use ( $context, $loggerInstance ) {
|
204 |
+
$locationParsed = parse_url( $location );
|
205 |
+
|
206 |
+
if ( $locationParsed === false || empty( $locationParsed['query'] ) ) {
|
207 |
+
return $location;
|
208 |
+
}
|
209 |
+
|
210 |
+
parse_str( $locationParsed['query'], $queryStringParsed);
|
211 |
+
|
212 |
+
if (empty($queryStringParsed)) {
|
213 |
+
return $location;
|
214 |
+
}
|
215 |
+
|
216 |
+
if (isset($queryStringParsed["updated"]) && $queryStringParsed["updated"]) {
|
217 |
+
// File was updated
|
218 |
+
$loggerInstance->infoMessage('theme_file_edited', $context);
|
219 |
+
} else {
|
220 |
+
// File was not updated. Unknown reason, but probably because could not be written.
|
221 |
+
}
|
222 |
+
|
223 |
+
return $location;
|
224 |
+
|
225 |
+
}, 10, 2 ); // add_filter
|
226 |
+
} // if post action update
|
227 |
+
}
|
228 |
+
|
229 |
+
|
230 |
+
public function getLogRowDetailsOutput( $row ) {
|
231 |
+
|
232 |
+
$context = $row->context;
|
233 |
+
$message_key = isset( $context["_message_key"] ) ? $context["_message_key"] : null;
|
234 |
+
|
235 |
+
if (! $message_key) {
|
236 |
+
return;
|
237 |
+
}
|
238 |
+
|
239 |
+
$out = '';
|
240 |
+
|
241 |
+
$diff_table_output = '';
|
242 |
+
|
243 |
+
if ( ! empty($context['new_file_contents']) && ! empty($context['old_file_contents']) ) {
|
244 |
+
if ( $context['new_file_contents'] !== $context['old_file_contents'] ) {
|
245 |
+
$diff_table_output .= sprintf(
|
246 |
+
'<tr><td>%1$s</td><td>%2$s</td></tr>',
|
247 |
+
__("File contents", "simple-history"),
|
248 |
+
simple_history_text_diff($context['old_file_contents'], $context['new_file_contents'])
|
249 |
+
);
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
if ( $diff_table_output ) {
|
254 |
+
|
255 |
+
$diff_table_output = '<table class="SimpleHistoryLogitem__keyValueTable">' . $diff_table_output . '</table>';
|
256 |
+
|
257 |
+
}
|
258 |
+
|
259 |
+
$out .= $diff_table_output;
|
260 |
+
|
261 |
+
return $out;
|
262 |
+
|
263 |
+
}
|
264 |
+
|
265 |
+
} // class
|
loggers/PluginUserSwitchingLogger.php
CHANGED
@@ -60,7 +60,6 @@ class PluginUserSwitchingLogger extends SimpleLogger {
|
|
60 |
// It is the old user who initiates the switching
|
61 |
"_initiator" => SimpleLoggerLogInitiators::WP_USER,
|
62 |
"_user_id" => $old_user_id,
|
63 |
-
|
64 |
"user_id" => $user_id,
|
65 |
"old_user_id" => $old_user_id,
|
66 |
"user_login_to" => $user_to->user_login,
|
60 |
// It is the old user who initiates the switching
|
61 |
"_initiator" => SimpleLoggerLogInitiators::WP_USER,
|
62 |
"_user_id" => $old_user_id,
|
|
|
63 |
"user_id" => $user_id,
|
64 |
"old_user_id" => $old_user_id,
|
65 |
"user_login_to" => $user_to->user_login,
|
loggers/SimplePluginLogger.php
CHANGED
@@ -59,12 +59,6 @@ class SimplePluginLogger extends SimpleLogger {
|
|
59 |
'simple-history'
|
60 |
),
|
61 |
|
62 |
-
'plugin_file_edited' => _x(
|
63 |
-
'Edited plugin file "{plugin_edited_file}"',
|
64 |
-
'Plugin file edited',
|
65 |
-
'simple-history'
|
66 |
-
),
|
67 |
-
|
68 |
'plugin_deleted' => _x(
|
69 |
'Deleted plugin "{plugin_name}"',
|
70 |
'Plugin files was deleted',
|
@@ -111,9 +105,6 @@ class SimplePluginLogger extends SimpleLogger {
|
|
111 |
_x("Failed plugin updates", "Plugin logger: search", "simple-history") => array(
|
112 |
'plugin_update_failed'
|
113 |
),
|
114 |
-
_x("Edited plugin files", "Plugin logger: search", "simple-history") => array(
|
115 |
-
'plugin_file_edited'
|
116 |
-
),
|
117 |
_x("Deleted plugins", "Plugin logger: search", "simple-history") => array(
|
118 |
'plugin_deleted'
|
119 |
),
|
@@ -152,9 +143,6 @@ class SimplePluginLogger extends SimpleLogger {
|
|
152 |
// Check hook extra for upgrader initiator
|
153 |
add_action( 'upgrader_process_complete', array( $this, "on_upgrader_process_complete" ), 10, 2 );
|
154 |
|
155 |
-
// Dirty check for things that we can't catch using filters or actions
|
156 |
-
add_action( 'admin_init', array( $this, "check_filterless_things" ) );
|
157 |
-
|
158 |
// Detect files removed
|
159 |
add_action( 'setted_transient', array( $this, 'on_setted_transient_for_remove_files' ), 10, 2 );
|
160 |
|
@@ -479,64 +467,6 @@ class SimplePluginLogger extends SimpleLogger {
|
|
479 |
|
480 |
}
|
481 |
|
482 |
-
function check_filterless_things() {
|
483 |
-
|
484 |
-
// Var is string with length 113: /wp-admin/plugin-editor.php?file=my-plugin%2Fviews%2Fplugin-file.php
|
485 |
-
$referer = wp_get_referer();
|
486 |
-
|
487 |
-
// contains key "path" with value like "/wp-admin/plugin-editor.php"
|
488 |
-
$referer_info = parse_url($referer);
|
489 |
-
|
490 |
-
if ( "/wp-admin/plugin-editor.php" === $referer_info["path"] ) {
|
491 |
-
|
492 |
-
// We are in plugin editor
|
493 |
-
// Check for plugin edit saved
|
494 |
-
if ( isset( $_POST["newcontent"] ) && isset( $_POST["action"] ) && "update" == $_POST["action"] && isset( $_POST["file"] ) && ! empty( $_POST["file"] ) ) {
|
495 |
-
|
496 |
-
// A file was edited
|
497 |
-
$file = $_POST["file"];
|
498 |
-
|
499 |
-
// $plugins = get_plugins();
|
500 |
-
// http://codex.wordpress.org/Function_Reference/wp_text_diff
|
501 |
-
|
502 |
-
// Generate a diff of changes
|
503 |
-
if ( ! class_exists( 'WP_Text_Diff_Renderer_Table' ) ) {
|
504 |
-
require_once( ABSPATH . WPINC . '/wp-diff.php' );
|
505 |
-
}
|
506 |
-
|
507 |
-
$original_file_contents = file_get_contents( WP_PLUGIN_DIR . "/" . $file );
|
508 |
-
$new_file_contents = wp_unslash( $_POST["newcontent"] );
|
509 |
-
|
510 |
-
$left_lines = explode("\n", $original_file_contents);
|
511 |
-
$right_lines = explode("\n", $new_file_contents);
|
512 |
-
$text_diff = new Text_Diff($left_lines, $right_lines);
|
513 |
-
|
514 |
-
$num_added_lines = $text_diff->countAddedLines();
|
515 |
-
$num_removed_lines = $text_diff->countDeletedLines();
|
516 |
-
|
517 |
-
// Generate a diff in classic diff format
|
518 |
-
$renderer = new Text_Diff_Renderer();
|
519 |
-
$diff = $renderer->render($text_diff);
|
520 |
-
|
521 |
-
$this->infoMessage(
|
522 |
-
'plugin_file_edited',
|
523 |
-
array(
|
524 |
-
"plugin_edited_file" => $file,
|
525 |
-
"plugin_edit_diff" => $diff,
|
526 |
-
"plugin_edit_num_added_lines" => $num_added_lines,
|
527 |
-
"plugin_edit_num_removed_lines" => $num_removed_lines,
|
528 |
-
)
|
529 |
-
);
|
530 |
-
|
531 |
-
$did_log = true;
|
532 |
-
|
533 |
-
}
|
534 |
-
|
535 |
-
}
|
536 |
-
|
537 |
-
|
538 |
-
}
|
539 |
-
|
540 |
/**
|
541 |
* Called when plugins is updated or installed
|
542 |
* Called from class-wp-upgrader.php
|
@@ -1184,7 +1114,7 @@ class SimplePluginLogger extends SimpleLogger {
|
|
1184 |
if ( "plugin_updated" == $message_key || "plugin_bulk_updated" == $message_key ) {
|
1185 |
|
1186 |
$link_title = esc_html_x("View changelog", "plugin logger: plugin info thickbox title", "simple-history");
|
1187 |
-
|
1188 |
if ( is_multisite() ) {
|
1189 |
$url = network_admin_url( "plugin-install.php?tab=plugin-information&plugin={$plugin_slug}&section=changelog&TB_iframe=true&width=772&height=550" );
|
1190 |
} else {
|
59 |
'simple-history'
|
60 |
),
|
61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
'plugin_deleted' => _x(
|
63 |
'Deleted plugin "{plugin_name}"',
|
64 |
'Plugin files was deleted',
|
105 |
_x("Failed plugin updates", "Plugin logger: search", "simple-history") => array(
|
106 |
'plugin_update_failed'
|
107 |
),
|
|
|
|
|
|
|
108 |
_x("Deleted plugins", "Plugin logger: search", "simple-history") => array(
|
109 |
'plugin_deleted'
|
110 |
),
|
143 |
// Check hook extra for upgrader initiator
|
144 |
add_action( 'upgrader_process_complete', array( $this, "on_upgrader_process_complete" ), 10, 2 );
|
145 |
|
|
|
|
|
|
|
146 |
// Detect files removed
|
147 |
add_action( 'setted_transient', array( $this, 'on_setted_transient_for_remove_files' ), 10, 2 );
|
148 |
|
467 |
|
468 |
}
|
469 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
470 |
/**
|
471 |
* Called when plugins is updated or installed
|
472 |
* Called from class-wp-upgrader.php
|
1114 |
if ( "plugin_updated" == $message_key || "plugin_bulk_updated" == $message_key ) {
|
1115 |
|
1116 |
$link_title = esc_html_x("View changelog", "plugin logger: plugin info thickbox title", "simple-history");
|
1117 |
+
|
1118 |
if ( is_multisite() ) {
|
1119 |
$url = network_admin_url( "plugin-install.php?tab=plugin-information&plugin={$plugin_slug}&section=changelog&TB_iframe=true&width=772&height=550" );
|
1120 |
} else {
|
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, 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.
|
8 |
|
9 |
View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
|
10 |
|
@@ -162,6 +162,13 @@ A simple way to see any uncommon activity, for example an increased number of lo
|
|
162 |
|
163 |
## Changelog
|
164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
= 2.15 (May 2017) =
|
166 |
|
167 |
- Use thumbnail version of PDF preview instead of full size image.
|
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.16
|
8 |
|
9 |
View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
|
10 |
|
162 |
|
163 |
## Changelog
|
164 |
|
165 |
+
= 2.16 (May 2017) =
|
166 |
+
|
167 |
+
- Added [WP-CLI](https://wp-cli.org) command for Simple History. Now you can write `wp simple-history list` to see the latest entries from the history log. For now `list` is the only available command. Let me know if you need more commands!
|
168 |
+
- Added support for logging edits to theme files and plugin files. When a file is edited you will also get a quick diff on the changes,
|
169 |
+
so you can see what CSS styles a client changed or what PHP changes they made in a plugin file.
|
170 |
+
- Removed the edit file logger from the plugin logger, because it did not always work (checked wrong wp path). Intead the new Theme and plugins logger mentioned above will take care of this.
|
171 |
+
|
172 |
= 2.15 (May 2017) =
|
173 |
|
174 |
- Use thumbnail version of PDF preview instead of full size image.
|