Simple History - Version 2.21

Version Description

(May 2018) =

  • Added support for Advanced Custom Fields (ACF): when a ACF Field or ACF Field Group is created or modified or deleted you will now get more details in the activity feed.
  • Changes to taxonomies/categories/tags now include a link to the modified term and to the category that the term belongs to.
  • The post types in the skip_posttypes filter are now also applied to trashed and untrashed posts (not only post edits, as before).
  • Don't log Jetpack sitemap updates. (Don't log updates to posttypes jp_sitemap, jp_sitemap_master and jp_img_sitemap, i.e. the post types used by Jetpack's Sitemap function.) Should fix https://wordpress.org/support/topic/jetpack-sitemap-logging/.
  • Don't log the taxonomies post_translations or term_translations, that are used by Polylang to store translation mappings. That contained md5-hashed strings and was not of any benefit (a separate logger for Polylang will come soon anyway).
  • Fix notice in theme logger because did not check if $_POST['sidebar'] was set. Fixes https://github.com/bonny/WordPress-Simple-History/issues/136.
  • Fix thumbnail title missing notice in post logger.
  • Fix PHP warning when a plugin was checked by WordPress for an update, but your WordPress install did not have the plugin folder for that plugin.
  • Fix unexpected single-quotations included in file name in Internet Explorer 11 (and possibly other versions) when exporting CSV/JSON file.
  • Fix filter/search log by specific users not working. Fixes https://wordpress.org/support/topic/show-activity-from-other-authors-only/.
  • Fix a notice in SimpleOptionsLogger.
  • Better CSS styling on dashboard.
  • Add filter simple_history/post_logger/post_updated/context that can be used to modify the context added by SimplePostLogger.
  • Add filter simple_history/post_logger/post_updated/ok_to_log that can be used to skip logging a post update.
  • Add filter simple_history/categories_logger/skip_taxonomies that can be used to modify what taxonomies to skip when logging updates to taxonomy terms.
Download this release

Release Info

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

Code changes from version 2.20 to 2.21

dropins/SimpleHistoryDonateDropin.php CHANGED
@@ -35,7 +35,7 @@ class SimpleHistoryDonateDropin {
35
 
36
  $links = array_merge(
37
  $links,
38
- array( sprintf( '<a href="http://eskapism.se/sida/donate/?utm_source=wordpress&utm_medium=pluginpage&utm_campaign=simplehistory">%1$s</a>', __( 'Donate', 'simple-history' ) ) )
39
  );
40
 
41
  }
@@ -71,9 +71,8 @@ class SimpleHistoryDonateDropin {
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
  }
35
 
36
  $links = array_merge(
37
  $links,
38
+ array( sprintf( '<a href="https://www.paypal.me/eskapism">%1$s</a>', __('Donate', "simple-history") ) )
39
  );
40
 
41
  }
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
+ 'https://www.paypal.me/eskapism'
 
76
  );
77
 
78
  }
dropins/SimpleHistoryExportDropin.php CHANGED
@@ -1,23 +1,30 @@
1
  <?php
2
 
3
- /*
4
- Dropin Name: Export
5
- Dropin Description: Adds a tab with export options
6
- Dropin URI: http://simple-history.com/
7
- Author: Pär Thernström
8
- */
9
-
10
  class SimpleHistoryExportDropin {
11
 
12
- // Simple History instance
 
 
 
 
13
  private $sh;
14
 
 
 
 
 
 
15
  public function __construct( $sh ) {
16
 
17
- // Set simple history variable
18
  $this->sh = $sh;
19
 
20
- // Add tab to settings page
21
  $sh->registerSettingsTab(array(
22
  'slug' => 'export',
23
  'name' => _x( 'Export', 'Export dropin: Tab name on settings page', 'simple-history' ),
@@ -64,18 +71,19 @@ class SimpleHistoryExportDropin {
64
 
65
  $fp = fopen( 'php://output', 'w' );
66
 
67
- // header("Content-Type: application/octet-stream");
 
68
  if ( 'csv' == $export_format ) {
69
 
70
  $filename = 'simple-history-export-' . time() . '.csv';
71
  header( 'Content-Type: text/plain' );
72
- header( "Content-Disposition: attachment; filename='{$filename}'" );
73
 
74
  } elseif ( 'json' == $export_format ) {
75
 
76
  $filename = 'simple-history-export-' . time() . '.json';
77
  header( 'Content-Type: application/json' );
78
- header( "Content-Disposition: attachment; filename='{$filename}'" );
79
 
80
  } elseif ( 'html' == $export_format ) {
81
 
1
  <?php
2
 
3
+ /**
4
+ * Dropin Name: Export
5
+ * Dropin Description: Adds a tab with export options
6
+ * Dropin URI: http://simple-history.com/
7
+ * Author: Pär Thernström
8
+ */
 
9
  class SimpleHistoryExportDropin {
10
 
11
+ /**
12
+ * Simple History instance.
13
+ *
14
+ * @var $sh
15
+ */
16
  private $sh;
17
 
18
+ /**
19
+ * Constructor.
20
+ *
21
+ * @param instance $sh Simple History instance.
22
+ */
23
  public function __construct( $sh ) {
24
 
 
25
  $this->sh = $sh;
26
 
27
+ // Add tab to settings page.
28
  $sh->registerSettingsTab(array(
29
  'slug' => 'export',
30
  'name' => _x( 'Export', 'Export dropin: Tab name on settings page', 'simple-history' ),
71
 
72
  $fp = fopen( 'php://output', 'w' );
73
 
74
+ $attachment_header_template = 'Content-Disposition: attachment; filename="%1$s"';
75
+
76
  if ( 'csv' == $export_format ) {
77
 
78
  $filename = 'simple-history-export-' . time() . '.csv';
79
  header( 'Content-Type: text/plain' );
80
+ header( sprintf( $attachment_header_template, $filename ) );
81
 
82
  } elseif ( 'json' == $export_format ) {
83
 
84
  $filename = 'simple-history-export-' . time() . '.json';
85
  header( 'Content-Type: application/json' );
86
+ header( sprintf( $attachment_header_template, $filename ) );
87
 
88
  } elseif ( 'html' == $export_format ) {
89
 
dropins/SimpleHistoryFilterDropin.css CHANGED
@@ -62,12 +62,12 @@
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
  }
@@ -83,6 +83,8 @@
83
  margin-left: 0;
84
  }
85
 
 
 
86
  /**
87
  * Search results in filter
88
  */
@@ -159,7 +161,6 @@
159
  visibility: hidden;
160
  opacity: 0;
161
  margin-left: 155px;
162
- margin-top: 1em;
163
  max-height: 0;
164
  overflow: hidden;
165
  transition: max-height .25s ease-in-out, opacity .25s ease-in-out, visibility 0s 1s;
@@ -169,6 +170,7 @@
169
  visibility: visible;
170
  opacity: 1;
171
  max-height: 150px;
 
172
  transition: max-height .25s ease-in-out, opacity .25s ease-in-out, visibility 0s 0s;
173
  }
174
 
@@ -196,4 +198,4 @@
196
 
197
  .SimpleHistory__filters .select2-results__option {
198
  margin: 0;
199
- }
62
 
63
  @media (min-width: 600px) {
64
  /* prevent "jump" during page load because of select elm changing to select2 */
65
+ .SimpleHistoryWrap .SimpleHistory__filters__filterRow {
66
  height: 41px;
67
  line-height: 41px;
68
  }
69
 
70
+ .SimpleHistoryWrap .wp-admin select[multiple].SimpleHistory__filters__filter--date {
71
  height: 2.25em;
72
  overflow: hidden;
73
  }
83
  margin-left: 0;
84
  }
85
 
86
+
87
+
88
  /**
89
  * Search results in filter
90
  */
161
  visibility: hidden;
162
  opacity: 0;
163
  margin-left: 155px;
 
164
  max-height: 0;
165
  overflow: hidden;
166
  transition: max-height .25s ease-in-out, opacity .25s ease-in-out, visibility 0s 1s;
170
  visibility: visible;
171
  opacity: 1;
172
  max-height: 150px;
173
+ margin-top: 1em;
174
  transition: max-height .25s ease-in-out, opacity .25s ease-in-out, visibility 0s 0s;
175
  }
176
 
198
 
199
  .SimpleHistory__filters .select2-results__option {
200
  margin: 0;
201
+ }
dropins/SimpleHistoryFilterDropin.js CHANGED
@@ -2,272 +2,269 @@
2
  *
3
  */
4
 
5
- var SimpleHistoryFilterDropin = (function($) {
6
-
7
- var $elms = {};
8
- var isFilteringActive = false;
9
- var activeFilters = {};
10
-
11
- function init() {
12
-
13
- addElements();
14
- addFetchListener();
15
-
16
- }
17
-
18
- function onDomReadyInit() {
19
-
20
- enhanceSelects();
21
- addListeners();
22
-
23
- }
24
-
25
- function addElements() {
26
-
27
- $elms.filter_container = $(".SimpleHistory__filters");
28
- $elms.filter_user = $elms.filter_container.find(".SimpleHistory__filters__filter--user");
29
- $elms.filter_button = $elms.filter_container.find(".js-SimpleHistoryFilterDropin-doFilter");
30
- $elms.filter_form = $elms.filter_container.find(".js-SimpleHistory__filters__form");
31
- $elms.show_more_filters_button = $elms.filter_container.find(".js-SimpleHistoryFilterDropin-showMoreFilters");
32
- $elms.more_filters_container = $elms.filter_container.find(".js-SimpleHistory__filters__moreFilters");
33
-
34
- }
35
-
36
- function addListeners() {
37
-
38
- $elms.filter_form.on("submit", onSubmitForm);
39
- $elms.show_more_filters_button.on("click", onClickMoreFilters);
40
-
41
- }
42
-
43
- function onClickMoreFilters() {
44
-
45
- //$elms.more_filters_container.toggleClass("is-visible");
46
- $elms.filter_container.toggleClass("is-showingMoreFilters");
47
-
48
- }
49
-
50
- function updateFilters() {
51
-
52
- // form serialize
53
- // search=apa&loglevels=critical&loglevels=alert&loggers=SimpleMediaLogger&loggers=SimpleMenuLogger&user=1&dates=2014-09 SimpleHistoryFilterDropin.js?ver=2.0:40
54
- var $search = $elms.filter_form.find("[name='search']");
55
- var $loglevels = $elms.filter_form.find("[name='loglevels']");
56
- var $messages = $elms.filter_form.find("[name='messages']");
57
- var $users = $elms.filter_form.find("[name='users']");
58
- var $dates = $elms.filter_form.find("[name='dates']");
59
-
60
- // Custom date range
61
- var $customDateRangeFromMM = $elms.filter_form.find("[name='from_mm']");
62
- var $customDateRangeFromJJ = $elms.filter_form.find("[name='from_jj']");
63
- var $customDateRangeFromAA = $elms.filter_form.find("[name='from_aa']");
64
- var $customDateRangeToMM = $elms.filter_form.find("[name='to_mm']");
65
- var $customDateRangeToJJ = $elms.filter_form.find("[name='to_jj']");
66
- var $customDateRangeToAA = $elms.filter_form.find("[name='to_aa']");
67
-
68
- // If any of our search boxes are filled in we consider ourself to be in search mode
69
- isFilteringActive = false;
70
- activeFilters = {};
71
-
72
- if ( $.trim( $search.val() )) {
73
- isFilteringActive = true;
74
- activeFilters.search = $search.val();
75
- }
76
-
77
- if ( $loglevels.val() && $loglevels.val().length ) {
78
- isFilteringActive = true;
79
- activeFilters.loglevels = $loglevels.val();
80
- }
81
-
82
- if ( $messages.val() && $messages.val().length ) {
83
- isFilteringActive = true;
84
- activeFilters.messages = $messages.val();
85
- }
86
-
87
- if ( $.trim( $users.val() )) {
88
- isFilteringActive = true;
89
- activeFilters.users = $users.val();
90
- }
91
-
92
- // Something is selected in the Dates dropdown
93
- if ( $dates.val() && $dates.val().length ) {
94
-
95
- isFilteringActive = true;
96
-
97
- // if dates val is selected but is "customRange" then dates is not active, but dateRange is
98
- if ("customRange" == $dates.val()) {
99
- activeFilters.date_from = $customDateRangeFromAA.val() + "-" + $customDateRangeFromMM.val() + "-" + $customDateRangeFromJJ.val() + " 00:00:00";
100
- activeFilters.date_to = $customDateRangeToAA.val() + "-" + $customDateRangeToMM.val() + "-" + $customDateRangeToJJ.val() + " 23:59:59";
101
- } else {
102
- activeFilters.dates = $dates.val();
103
- }
104
-
105
- }
106
- }
107
-
108
- function onSubmitForm(e) {
109
-
110
- e.preventDefault();
111
-
112
- // updateFilters();
113
-
114
- // Reload the log rows collection
115
- simple_history.logRowsCollection.reload();
116
-
117
- }
118
-
119
- function addFetchListener() {
120
-
121
- $(document).on("SimpleHistory:mainViewInitBeforeLoadRows", function() {
122
-
123
- // Modify query string parameters before the log rows collection fetches/syncs
124
- simple_history.logRowsCollection.on("before_fetch", modifyFetchData);
125
-
126
- });
127
-
128
- // Alter api args used by new log rows notifier
129
- $(document).on("SimpleHistory:NewRowsNotifier:apiArgs", modifyNewRowsNotifierApiArgs);
130
-
131
-
132
- }
133
-
134
- function modifyNewRowsNotifierApiArgs(e, apiArgs) {
135
-
136
- if (isFilteringActive) {
137
-
138
- apiArgs = _.extend(apiArgs, activeFilters);
139
-
140
- }
141
-
142
- }
143
-
144
- // called each time the log is reloaded
145
- function modifyFetchData(collection, url_data) {
146
-
147
- updateFilters();
148
-
149
- if (isFilteringActive) {
150
-
151
- url_data = _.extend(url_data, activeFilters);
152
-
153
- }
154
-
155
- }
156
-
157
- function enhanceSelects() {
158
-
159
- $elms.filter_user.select2({
160
- minimumInputLength: 2,
161
- allowClear: true,
162
- placeholder: "All users",
163
- ajax: {
164
- url: ajaxurl,
165
- dataType: "json",
166
- cache: true,
167
- data: function (term, page) {
168
- return {
169
- q: term.term, // search term
170
- page_limit: 10,
171
- action: "simple_history_filters_search_user"
172
- };
173
- },
174
- // parse the results into the format expected by Select2.
175
- processResults: function (data, page) {
176
- // since we are using custom formatting functions we do not need to alter remote JSON data
177
- return data.data;
178
- }
179
- },
180
- templateResult: formatUsers,
181
- initSelection: function(elm, callback) {
182
- // called on init if value attribute on input is set
183
- var $elm = $(elm);
184
- var value = $elm.val();
185
- var default_user_data = $elms.filter_user.data("default-user-data");
186
-
187
- callback(default_user_data);
188
- },
189
- templateSelection: formatUsers,
190
- escapeMarkup: function(m) {
191
- return m;
192
- },
193
- multiple: true
194
- });
195
-
196
- $(".SimpleHistory__filters__filter--logger").select2();
197
-
198
- var $filterDate = $(".SimpleHistory__filters__filter--date");
199
- $filterDate.select2();
200
- $filterDate.on("select2-selecting change", onDatesFilterSelect);
201
-
202
- $(".SimpleHistory__filters__filter--loglevel").select2({
203
- templateResult: formatLoglevel,
204
- templateSelection: formatLoglevel,
205
- escapeMarkup: function(m) { return m; }
206
- });
207
-
208
- }
209
-
210
- /**
211
- * Fired when something is selected in the date filter
212
- * When "Custom range..." is selected then we show the "from" .. "to" date fields
213
- */
214
- function onDatesFilterSelect(e, elm) {
215
-
216
- var $filterDate = $("select.SimpleHistory__filters__filter--date");
217
- var val = $filterDate.val();
218
-
219
- if (val === "customRange") {
220
- // show custom date fields
221
- $elms.filter_container.addClass("is-customDateFilterActive");
222
- } else {
223
- // hide custom date fields
224
- $elms.filter_container.removeClass("is-customDateFilterActive");
225
- }
226
-
227
- }
228
-
229
- function formatUsers(userdata) {
230
- if (userdata.loading) {
231
- return userdata.text;
232
- }
233
-
234
- var html = "";
235
-
236
- html += "<div class='SimpleHistory__filters__userfilter__gravatar'>";
237
- html += userdata.gravatar;
238
- html += "</div>";
239
- html += "<div class='SimpleHistory__filters__userfilter__primary'>";
240
- html += userdata.user_email;
241
- html += "</div>";
242
- html += "<div class='SimpleHistory__filters__userfilter__secondary'>";
243
- html += userdata.user_login;
244
- html += "</div>";
245
-
246
- return html;
247
- }
248
-
249
- /**
250
- * Used by templateResult and templateSelection
251
- */
252
- function formatLoglevel(loglevel) {
253
- var originalOption = loglevel.element;
254
- var $originalOption = $(originalOption);
255
- var color = $originalOption.data("color");
256
-
257
- var html = "<span style=\"border-radius: 50%; border: 1px solid rgba(0,0,0,.1); margin-right: 5px; width: .75em; height: .75em; line-height: 1; display: inline-block; background-color: " + $originalOption.data('color') + "; '\"></span>" + loglevel.text;
258
- return html;
259
- }
260
-
261
- return {
262
- init: init,
263
- onDomReadyInit: onDomReadyInit,
264
- $elms: $elms
265
- };
266
-
267
- })(jQuery);
268
-
269
- SimpleHistoryFilterDropin.init();
270
-
271
- jQuery(document).ready(function() {
272
- SimpleHistoryFilterDropin.onDomReadyInit();
273
- });
2
  *
3
  */
4
 
5
+ var SimpleHistoryFilterDropin = (function ($) {
6
+ var $elms = {}
7
+ var isFilteringActive = false
8
+ var activeFilters = {}
9
+
10
+ function init () {
11
+ addElements()
12
+ addFetchListener()
13
+ }
14
+
15
+ function onDomReadyInit () {
16
+ enhanceSelects()
17
+ addListeners()
18
+ }
19
+
20
+ function addElements () {
21
+ $elms.filter_container = $('.SimpleHistory__filters')
22
+ $elms.filter_user = $elms.filter_container.find(
23
+ '.SimpleHistory__filters__filter--user'
24
+ )
25
+ $elms.filter_button = $elms.filter_container.find(
26
+ '.js-SimpleHistoryFilterDropin-doFilter'
27
+ )
28
+ $elms.filter_form = $elms.filter_container.find(
29
+ '.js-SimpleHistory__filters__form'
30
+ )
31
+ $elms.show_more_filters_button = $elms.filter_container.find(
32
+ '.js-SimpleHistoryFilterDropin-showMoreFilters'
33
+ )
34
+ $elms.more_filters_container = $elms.filter_container.find(
35
+ '.js-SimpleHistory__filters__moreFilters'
36
+ )
37
+ }
38
+
39
+ function addListeners () {
40
+ $elms.filter_form.on('submit', onSubmitForm)
41
+ $elms.show_more_filters_button.on('click', onClickMoreFilters)
42
+ }
43
+
44
+ function onClickMoreFilters () {
45
+ // $elms.more_filters_container.toggleClass("is-visible");
46
+ $elms.filter_container.toggleClass('is-showingMoreFilters')
47
+ }
48
+
49
+ function updateFilters () {
50
+ // form serialize
51
+ // search=apa&loglevels=critical&loglevels=alert&loggers=SimpleMediaLogger&loggers=SimpleMenuLogger&user=1&dates=2014-09 SimpleHistoryFilterDropin.js?ver=2.0:40
52
+ var $search = $elms.filter_form.find("[name='search']")
53
+ var $loglevels = $elms.filter_form.find("[name='loglevels']")
54
+ var $messages = $elms.filter_form.find("[name='messages']")
55
+ var $users = $elms.filter_form.find("[name='users']")
56
+ var $dates = $elms.filter_form.find("[name='dates']")
57
+
58
+ // Custom date range
59
+ var $customDateRangeFromMM = $elms.filter_form.find("[name='from_mm']")
60
+ var $customDateRangeFromJJ = $elms.filter_form.find("[name='from_jj']")
61
+ var $customDateRangeFromAA = $elms.filter_form.find("[name='from_aa']")
62
+ var $customDateRangeToMM = $elms.filter_form.find("[name='to_mm']")
63
+ var $customDateRangeToJJ = $elms.filter_form.find("[name='to_jj']")
64
+ var $customDateRangeToAA = $elms.filter_form.find("[name='to_aa']")
65
+
66
+ // If any of our search boxes are filled in we consider ourself to be in search mode
67
+ isFilteringActive = false
68
+ activeFilters = {}
69
+
70
+ if ($.trim($search.val())) {
71
+ isFilteringActive = true
72
+ activeFilters.search = $search.val()
73
+ }
74
+
75
+ if ($loglevels.val() && $loglevels.val().length) {
76
+ isFilteringActive = true
77
+ activeFilters.loglevels = $loglevels.val()
78
+ }
79
+
80
+ if ($messages.val() && $messages.val().length) {
81
+ isFilteringActive = true
82
+ activeFilters.messages = $messages.val()
83
+ }
84
+
85
+ if ($.trim($users.val())) {
86
+ isFilteringActive = true
87
+ activeFilters.users = $users.val()
88
+ }
89
+
90
+ // Something is selected in the Dates dropdown
91
+ if ($dates.val() && $dates.val().length) {
92
+ isFilteringActive = true
93
+
94
+ // if dates val is selected but is "customRange" then dates is not active, but dateRange is
95
+ if ($dates.val() == 'customRange') {
96
+ activeFilters.date_from =
97
+ $customDateRangeFromAA.val() +
98
+ '-' +
99
+ $customDateRangeFromMM.val() +
100
+ '-' +
101
+ $customDateRangeFromJJ.val() +
102
+ ' 00:00:00'
103
+ activeFilters.date_to =
104
+ $customDateRangeToAA.val() +
105
+ '-' +
106
+ $customDateRangeToMM.val() +
107
+ '-' +
108
+ $customDateRangeToJJ.val() +
109
+ ' 23:59:59'
110
+ } else {
111
+ activeFilters.dates = $dates.val()
112
+ }
113
+ }
114
+ }
115
+
116
+ function onSubmitForm (e) {
117
+ e.preventDefault()
118
+
119
+ // updateFilters();
120
+
121
+ // Reload the log rows collection
122
+ simple_history.logRowsCollection.reload()
123
+ }
124
+
125
+ function addFetchListener () {
126
+ $(document).on('SimpleHistory:mainViewInitBeforeLoadRows', function () {
127
+ // Modify query string parameters before the log rows collection fetches/syncs
128
+ simple_history.logRowsCollection.on('before_fetch', modifyFetchData)
129
+ })
130
+
131
+ // Alter api args used by new log rows notifier
132
+ $(document).on(
133
+ 'SimpleHistory:NewRowsNotifier:apiArgs',
134
+ modifyNewRowsNotifierApiArgs
135
+ )
136
+ }
137
+
138
+ function modifyNewRowsNotifierApiArgs (e, apiArgs) {
139
+ if (isFilteringActive) {
140
+ apiArgs = _.extend(apiArgs, activeFilters)
141
+ }
142
+ }
143
+
144
+ // called each time the log is reloaded
145
+ function modifyFetchData (collection, url_data) {
146
+ updateFilters()
147
+
148
+ if (isFilteringActive) {
149
+ url_data = _.extend(url_data, activeFilters)
150
+ }
151
+ }
152
+
153
+ function enhanceSelects () {
154
+ $elms.filter_user.select2({
155
+ minimumInputLength: 2,
156
+ allowClear: true,
157
+ placeholder: 'All users',
158
+ ajax: {
159
+ url: ajaxurl,
160
+ dataType: 'json',
161
+ cache: true,
162
+ data: function (term, page) {
163
+ return {
164
+ q: term.term, // search term
165
+ page_limit: 10,
166
+ action: 'simple_history_filters_search_user'
167
+ }
168
+ },
169
+ // parse the results into the format expected by Select2.
170
+ processResults: function (data, page) {
171
+ // since we are using custom formatting functions we do not need to alter remote JSON data
172
+ return data.data
173
+ }
174
+ },
175
+ templateResult: formatUsers,
176
+ initSelection: function (elm, callback) {
177
+ // called on init if value attribute on input is set
178
+ var $elm = $(elm)
179
+ var value = $elm.val()
180
+ var default_user_data = $elms.filter_user.data('default-user-data')
181
+
182
+ callback(default_user_data)
183
+ },
184
+ templateSelection: formatUsers,
185
+ escapeMarkup: function (m) {
186
+ return m
187
+ },
188
+ multiple: true
189
+ })
190
+
191
+ $('.SimpleHistory__filters__filter--logger').select2()
192
+
193
+ var $filterDate = $('.SimpleHistory__filters__filter--date')
194
+ $filterDate.select2()
195
+ $filterDate.on('select2-selecting change', onDatesFilterSelect)
196
+
197
+ $('.SimpleHistory__filters__filter--loglevel').select2({
198
+ templateResult: formatLoglevel,
199
+ templateSelection: formatLoglevel,
200
+ escapeMarkup: function (m) {
201
+ return m
202
+ }
203
+ })
204
+ }
205
+
206
+ /**
207
+ * Fired when something is selected in the date filter
208
+ * When "Custom range..." is selected then we show the "from" .. "to" date fields
209
+ */
210
+ function onDatesFilterSelect (e, elm) {
211
+ var $filterDate = $('select.SimpleHistory__filters__filter--date')
212
+ var val = $filterDate.val()
213
+
214
+ if (val === 'customRange') {
215
+ // show custom date fields
216
+ $elms.filter_container.addClass('is-customDateFilterActive')
217
+ } else {
218
+ // hide custom date fields
219
+ $elms.filter_container.removeClass('is-customDateFilterActive')
220
+ }
221
+ }
222
+
223
+ function formatUsers (userdata) {
224
+ if (userdata.loading) {
225
+ return userdata.text
226
+ }
227
+
228
+ var html = ''
229
+
230
+ html += "<div class='SimpleHistory__filters__userfilter__gravatar'>"
231
+ html += userdata.gravatar
232
+ html += '</div>'
233
+ html += "<div class='SimpleHistory__filters__userfilter__primary'>"
234
+ html += userdata.user_email
235
+ html += '</div>'
236
+ html += "<div class='SimpleHistory__filters__userfilter__secondary'>"
237
+ html += userdata.user_login
238
+ html += '</div>'
239
+
240
+ return html
241
+ }
242
+
243
+ /**
244
+ * Used by templateResult and templateSelection
245
+ */
246
+ function formatLoglevel (loglevel) {
247
+ var originalOption = loglevel.element
248
+ var $originalOption = $(originalOption)
249
+ var color = $originalOption.data('color')
250
+
251
+ var html =
252
+ '<span style="border-radius: 50%; border: 1px solid rgba(0,0,0,.1); margin-right: 5px; width: .75em; height: .75em; line-height: 1; display: inline-block; background-color: ' +
253
+ $originalOption.data('color') +
254
+ "; '\"></span>" +
255
+ loglevel.text
256
+ return html
257
+ }
258
+
259
+ return {
260
+ init: init,
261
+ onDomReadyInit: onDomReadyInit,
262
+ $elms: $elms
263
+ }
264
+ })(jQuery)
265
+
266
+ SimpleHistoryFilterDropin.init()
267
+
268
+ jQuery(document).ready(function () {
269
+ SimpleHistoryFilterDropin.onDomReadyInit()
270
+ })
 
 
 
dropins/SimpleHistoryPluginPatchesDropin.php CHANGED
@@ -52,7 +52,6 @@ class SimpleHistoryPluginPatchesDropin {
52
  }
53
 
54
  // ok, this is a non-admin, cron-running post update for the ai1ec_event post type, so cancel the logging
55
- error_log( 'ok, cancel ai1ec_event log' );
56
  $doLog = false;
57
 
58
  return $doLog;
@@ -108,15 +107,9 @@ class SimpleHistoryPluginPatchesDropin {
108
  }
109
 
110
  // There. All checked. Now cancel the logging.
111
- error_log( 'ok, cancel nextgen gallery log' );
112
  $doLog = false;
113
 
114
- // error_log(simpleHistory::json_encode( $context ));
115
- // error_log(simpleHistory::json_encode( $loggerInstance ));
116
- // error_log(simpleHistory::json_encode( is_admin() ));
117
- // error_log( __METHOD__ . " canceled logging" );
118
  return $doLog;
119
-
120
  }
121
 
122
 
52
  }
53
 
54
  // ok, this is a non-admin, cron-running post update for the ai1ec_event post type, so cancel the logging
 
55
  $doLog = false;
56
 
57
  return $doLog;
107
  }
108
 
109
  // There. All checked. Now cancel the logging.
 
110
  $doLog = false;
111
 
 
 
 
 
112
  return $doLog;
 
113
  }
114
 
115
 
inc/SimpleHistory.php CHANGED
@@ -516,9 +516,7 @@ class SimpleHistory {
516
 
517
  if ( ! wp_next_scheduled( 'simple_history/maybe_purge_db' ) ) {
518
  wp_schedule_event( time(), 'daily', 'simple_history/maybe_purge_db' );
519
- // error_log("not scheduled, so do schedule");
520
  } else {
521
- // error_log("is scheduled");
522
  }
523
 
524
  // Remove old schedule (only author dev sites should have it)
@@ -989,13 +987,14 @@ class SimpleHistory {
989
  $loggersDir . 'FileEditsLogger.php',
990
 
991
  // Loggers for third party plugins
992
- $loggersDir . 'PluginUserSwitchingLogger.php',
993
- $loggersDir . 'PluginEnableMediaReplaceLogger.php',
994
- $loggersDir . 'Plugin_UltimateMembers_Logger.php',
995
- $loggersDir . 'Plugin_LimitLoginAttempts.php',
996
- $loggersDir . 'Plugin_Redirection.php',
997
- $loggersDir . 'Plugin_DuplicatePost.php',
998
- );
 
999
 
1000
  // SimpleLogger.php must be loaded first and always since the other loggers extend it
1001
  // Include it manually so risk of anyone using filters or similar disables it
@@ -2362,6 +2361,7 @@ Because Simple History was just recently installed, this feed does not contain m
2362
  $message = _nx(
2363
  'Simple History removed one event that were older than {days} days',
2364
  'Simple History removed {num_rows} events that were older than {days} days',
 
2365
  'Database is being cleared automagically',
2366
  'simple-history'
2367
  );
@@ -2658,32 +2658,14 @@ Because Simple History was just recently installed, this feed does not contain m
2658
  }
2659
 
2660
  $logRowContextKeysToShow = array_fill_keys( array_keys( (array) $oneLogRow->context ), true );
2661
- /*
2662
- error_log($this->json_encode($logRowContextKeysToShow));
2663
- Marker - 2 maj 2015 20:51:54
2664
- [02-May-2015 18:51:57 UTC] {
2665
- "post_id": true,
2666
- "post_type": true,
2667
- "post_title": true,
2668
- "post_prev_post_title": true,
2669
- "post_new_post_title": true,
2670
- "post_prev_post_name": true,
2671
- "post_new_post_name": true,
2672
- "_message_key": true,
2673
- "_user_id": true,
2674
- "_user_login": true,
2675
- "_user_email": true,
2676
- "_server_remote_addr": true,
2677
- "_server_http_referer": true
2678
- }
2679
- */
2680
  /**
2681
  * Filter what keys to show from the row context
2682
  *
2683
  * Array is in format
2684
  *
2685
- * Array
2686
- * (
2687
  * [plugin_slug] => 1
2688
  * [plugin_name] => 1
2689
  * [plugin_title] => 1
@@ -2902,7 +2884,11 @@ Because Simple History was just recently installed, this feed does not contain m
2902
  }
2903
 
2904
 
2905
- public function getInstantiatedLoggerBySlug( $slug = '' ) {
 
 
 
 
2906
 
2907
  if ( empty( $slug ) ) {
2908
  return false;
516
 
517
  if ( ! wp_next_scheduled( 'simple_history/maybe_purge_db' ) ) {
518
  wp_schedule_event( time(), 'daily', 'simple_history/maybe_purge_db' );
 
519
  } else {
 
520
  }
521
 
522
  // Remove old schedule (only author dev sites should have it)
987
  $loggersDir . 'FileEditsLogger.php',
988
 
989
  // Loggers for third party plugins
990
+ $loggersDir . "PluginUserSwitchingLogger.php",
991
+ $loggersDir . "PluginEnableMediaReplaceLogger.php",
992
+ $loggersDir . "Plugin_UltimateMembers_Logger.php",
993
+ $loggersDir . "Plugin_LimitLoginAttempts.php",
994
+ $loggersDir . "Plugin_Redirection.php",
995
+ $loggersDir . "Plugin_DuplicatePost.php",
996
+ $loggersDir . "Plugin_ACF.php"
997
+ );
998
 
999
  // SimpleLogger.php must be loaded first and always since the other loggers extend it
1000
  // Include it manually so risk of anyone using filters or similar disables it
2361
  $message = _nx(
2362
  'Simple History removed one event that were older than {days} days',
2363
  'Simple History removed {num_rows} events that were older than {days} days',
2364
+ sizeof( $ids_to_delete ),
2365
  'Database is being cleared automagically',
2366
  'simple-history'
2367
  );
2658
  }
2659
 
2660
  $logRowContextKeysToShow = array_fill_keys( array_keys( (array) $oneLogRow->context ), true );
2661
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2662
  /**
2663
  * Filter what keys to show from the row context
2664
  *
2665
  * Array is in format
2666
  *
2667
+ * Array
2668
+ * (
2669
  * [plugin_slug] => 1
2670
  * [plugin_name] => 1
2671
  * [plugin_title] => 1
2884
  }
2885
 
2886
 
2887
+ /**
2888
+ * @param string $slug
2889
+ * @return mixed logger instance if found, bool false if logger not found
2890
+ */
2891
+ public function getInstantiatedLoggerBySlug( $slug = "" ) {
2892
 
2893
  if ( empty( $slug ) ) {
2894
  return false;
inc/SimpleHistoryLogQuery.php CHANGED
@@ -20,7 +20,6 @@ class SimpleHistoryLogQuery {
20
  }
21
 
22
  public function query( $args ) {
23
-
24
  $defaults = array(
25
 
26
  // overview | occasions
@@ -638,7 +637,12 @@ class SimpleHistoryLogQuery {
638
 
639
  }
640
 
641
- // users, comma separated
 
 
 
 
 
642
  if ( ! empty( $args['users'] ) && is_string( $args['users'] ) ) {
643
 
644
  $users = explode( ',', $args['users'] );
20
  }
21
 
22
  public function query( $args ) {
 
23
  $defaults = array(
24
 
25
  // overview | occasions
637
 
638
  }
639
 
640
+ // If users is array, make it comma separated.
641
+ if ( isset( $args['users'] ) && is_array( $args['users'] ) ) {
642
+ $args['users'] = implode( ',', $args['users'] );
643
+ }
644
+
645
+ // Users, comma separated.
646
  if ( ! empty( $args['users'] ) && is_string( $args['users'] ) ) {
647
 
648
  $users = explode( ',', $args['users'] );
index.php CHANGED
@@ -4,8 +4,8 @@
4
  * 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.20
9
  * Author: Pär Thernström
10
  * Author URI: http://simple-history.com/
11
  * License: GPL2
@@ -48,7 +48,7 @@ if ( $ok_php_version && $ok_wp_version ) {
48
  */
49
 
50
  if ( ! defined( 'SIMPLE_HISTORY_VERSION' ) ) {
51
- define( 'SIMPLE_HISTORY_VERSION', '2.20' );
52
  }
53
 
54
  if ( ! defined( 'SIMPLE_HISTORY_PATH' ) ) {
4
  * 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.21
9
  * Author: Pär Thernström
10
  * Author URI: http://simple-history.com/
11
  * License: GPL2
48
  */
49
 
50
  if ( ! defined( 'SIMPLE_HISTORY_VERSION' ) ) {
51
+ define( 'SIMPLE_HISTORY_VERSION', '2.21' );
52
  }
53
 
54
  if ( ! defined( 'SIMPLE_HISTORY_PATH' ) ) {
loggers/AvailableUpdatesLogger.php CHANGED
@@ -143,11 +143,22 @@ if ( ! class_exists( 'AvailableUpdatesLogger' ) ) {
143
  // For each available update
144
  foreach ( $updates->response as $key => $data ) {
145
 
146
- $plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $key, true, false );
 
 
 
 
 
 
 
 
 
 
 
147
 
148
  $plugin_new_version = isset( $data->new_version ) ? $data->new_version : '';
149
 
150
- // check if this plugin and this version has been checked/logged already
151
  if ( ! array_key_exists( $key, $checked_updates ) ) {
152
  $checked_updates[ $key ] = array(
153
  'checked_version' => null,
@@ -178,7 +189,6 @@ if ( ! class_exists( 'AvailableUpdatesLogger' ) ) {
178
 
179
  } // function
180
 
181
-
182
  function on_setted_update_update_themes( $updates ) {
183
 
184
  if ( empty( $updates->response ) || ! is_array( $updates->response ) ) {
143
  // For each available update
144
  foreach ( $updates->response as $key => $data ) {
145
 
146
+ // Make sure plugin directory exists or get_plugin_data will
147
+ // give warning like
148
+ // "PHP Warning: fread() expects parameter 1 to be resource, boolean given in /wp/wp-includes/functions.php on line 4837"
149
+ $file = WP_PLUGIN_DIR . '/' . $key;
150
+ $fp = fopen( $file, 'r' );
151
+
152
+ // Continue with next plugin if plugin file did not exist.
153
+ if (false === $fp) {
154
+ continue;
155
+ }
156
+
157
+ $plugin_info = get_plugin_data( $file, true, false );
158
 
159
  $plugin_new_version = isset( $data->new_version ) ? $data->new_version : '';
160
 
161
+ // Check if this plugin and this version has been checked/logged already.
162
  if ( ! array_key_exists( $key, $checked_updates ) ) {
163
  $checked_updates[ $key ] = array(
164
  'checked_version' => null,
189
 
190
  } // function
191
 
 
192
  function on_setted_update_update_themes( $updates ) {
193
 
194
  if ( empty( $updates->response ) || ! is_array( $updates->response ) ) {
loggers/FileEditsLogger.php CHANGED
@@ -78,11 +78,8 @@ class FileEditsLogger extends SimpleLogger {
78
  '_occasionsID' => __CLASS__ . '/' . __FUNCTION__ . "/file-edit/$plugin_file/$file",
79
  );
80
 
81
- // ddd($_POST, $context, $action, $file, $plugin, $phperror, $fileNewContents, $scrollto);
82
  $loggerInstance = $this;
83
  add_filter( 'wp_redirect', function ( $location, $status ) use ( $context, $loggerInstance ) {
84
- error_log( $location );
85
-
86
  $locationParsed = parse_url( $location );
87
 
88
  if ( $locationParsed === false || empty( $locationParsed['query'] ) ) {
78
  '_occasionsID' => __CLASS__ . '/' . __FUNCTION__ . "/file-edit/$plugin_file/$file",
79
  );
80
 
 
81
  $loggerInstance = $this;
82
  add_filter( 'wp_redirect', function ( $location, $status ) use ( $context, $loggerInstance ) {
 
 
83
  $locationParsed = parse_url( $location );
84
 
85
  if ( $locationParsed === false || empty( $locationParsed['query'] ) ) {
loggers/Plugin_ACF.php ADDED
@@ -0,0 +1,1032 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ defined( 'ABSPATH' ) || die();
4
+
5
+ // Only enable in development mode.
6
+ if ( ! defined( 'SIMPLE_HISTORY_DEV' ) || ! SIMPLE_HISTORY_DEV ) {
7
+ return;
8
+ }
9
+
10
+ /**
11
+ * Logger for the Advanced Custom Fields (ACF) plugin
12
+ * https://sv.wordpress.org/plugins/advanced-custom-fields/
13
+ *
14
+ * @package SimpleHistory
15
+ * @since 2.21
16
+ */
17
+ if ( ! class_exists( 'Plugin_ACF' ) ) {
18
+
19
+ /**
20
+ * Class for ACF logging.
21
+ */
22
+ class Plugin_ACF extends SimpleLogger {
23
+
24
+ /**
25
+ * The slug for this logger.
26
+ *
27
+ * @var string $slug
28
+ */
29
+ public $slug = __CLASS__;
30
+
31
+ /**
32
+ * Will contain field groups and fields, before and after post save.
33
+ *
34
+ * @var string $oldAndNewFieldGroupsAndFields
35
+ */
36
+ private $oldAndNewFieldGroupsAndFields = array(
37
+ 'fieldGroup' => array(
38
+ 'old' => null,
39
+ 'new' => null,
40
+ ),
41
+ 'modifiedFields' => array(
42
+ 'old' => null,
43
+ 'new' => null,
44
+ ),
45
+ 'addedFields' => array(),
46
+ 'deletedFields' => array(),
47
+ );
48
+
49
+ /**
50
+ * Will contain the post data before save, i.e. the previous version of the post.
51
+ *
52
+ * @var string $oldPostData
53
+ */
54
+ private $oldPostData = array();
55
+
56
+ /**
57
+ * Get info for this logger.
58
+ *
59
+ * @return array Array with info about the logger.
60
+ */
61
+ public function getInfo() {
62
+ $arr_info = array(
63
+ 'name' => 'Plugin ACF',
64
+ 'description' => _x( 'Logs ACF stuff', 'Logger: Plugin ACF', 'simple-history' ),
65
+ 'name_via' => _x( 'Using plugin ACF', 'Logger: Plugin ACF', 'simple-history' ),
66
+ 'capability' => 'manage_options',
67
+ );
68
+
69
+ return $arr_info;
70
+ }
71
+
72
+ /**
73
+ * Method called when logger is loaded.
74
+ */
75
+ public function loaded() {
76
+
77
+ // Bail if no ACF found.
78
+ if ( ! function_exists( 'acf_verify_nonce' ) ) {
79
+ return;
80
+ }
81
+
82
+ // Remove ACF Fields from the post types that postlogger logs.
83
+ add_filter( 'simple_history/post_logger/skip_posttypes', array( $this, 'remove_acf_from_postlogger') );
84
+
85
+ // Get prev version of acf field group.
86
+ // This is called before transition_post_status.
87
+ add_filter( 'wp_insert_post_data', array( $this, 'on_wp_insert_post_data' ), 10, 2 );
88
+
89
+ // Store old and new field data when a post is saved.
90
+ add_action( 'transition_post_status', array( $this, 'on_transition_post_status' ), 5, 3 );
91
+
92
+ // Append ACF data to post context
93
+ add_filter( 'simple_history/post_logger/post_updated/context', array( $this, 'on_post_updated_context' ), 10, 2 );
94
+
95
+ // Add ACF diff data to activity feed detailed output.
96
+ add_filter( 'simple_history/post_logger/post_updated/diff_table_output', array( $this, 'on_diff_table_output_field_group' ), 10, 2 );
97
+
98
+ // Store prev ACF field values before new values are added.
99
+ // Called from filter admin_action_editpost that is fired at top of admin.php
100
+ add_action( 'admin_action_editpost', array( $this, 'on_admin_action_editpost' ) );
101
+
102
+ // Fired when ACF saves a post. Adds ACF context to logged row.
103
+ add_filter( 'acf/save_post', array( $this, 'on_acf_save_post' ), 50 );
104
+
105
+ // Fired after a log row is inserted. Add filter so field group save is is not logged again.
106
+ add_action( 'simple_history/log/inserted', array( $this, 'on_log_inserted' ), 10, 3 );
107
+ }
108
+
109
+ /**
110
+ * Fired after a log row is inserted.
111
+ */
112
+ public function on_log_inserted( $context, $data_parent_row, $simple_history_instance ) {
113
+ $message_key = ! empty( $context['_message_key'] ) ? $context['_message_key'] : false;
114
+ $logger = ! empty( $data_parent_row['logger'] ) ? $data_parent_row['logger'] : false;
115
+ $post_id = ! empty( $context['post_id'] ) ? $context['post_id'] : false;
116
+ $post_type = ! empty( $context['post_type'] ) ? $context['post_type'] : false;
117
+
118
+ // Bail if not all required vars are set.
119
+ if ( ! $message_key || ! $logger || ! $post_id || ! $post_type ) {
120
+ return;
121
+ }
122
+
123
+ // Only act when logger was SimplePostLogger.
124
+ if ( $logger !== 'SimplePostLogger' ) {
125
+ return;
126
+ }
127
+
128
+ // Only act when the saved type was a ACF Field Group.
129
+ if ( $post_type !== 'acf-field-group' ) {
130
+ return;
131
+ }
132
+
133
+ // Ok, a row was inserted using the log function on SimplePostLogger,
134
+ // now ACF will call save_post again and trigger
135
+ // another log of the same row. To prevent this we
136
+ // now add a filter to prevent the next log.
137
+ add_filter( 'simple_history/post_logger/post_updated/ok_to_log', array( $this, 'prevent_second_acf_field_group_post_save_log' ), 10, 4 );
138
+ }
139
+
140
+ /**
141
+ * Fired from SimpleLogger action 'simple_history/post_logger/post_updated/ok_to_log' and added only after
142
+ * a row already has been logged.
143
+ *
144
+ * This function checks if post type logged by SimplePostLogger is a ACF Field Group, and if it is
145
+ * then don't log that log. This way we prevent the post logger from logging the field group changes twice.
146
+ */
147
+ public function prevent_second_acf_field_group_post_save_log($ok_to_log, $new_status, $old_status, $post) {
148
+ if (isset($post->post_type) && $post->post_type === 'acf-field-group') {
149
+ $ok_to_log = false;
150
+ }
151
+
152
+ return $ok_to_log;
153
+ }
154
+
155
+ /**
156
+ * Append info about changes in ACF fields input,
157
+ * i.e. store all info we later use to show changes that a user has done.
158
+ *
159
+ * Called when ACF saves a post.
160
+ *
161
+ * @param int $post_id ID of post that is being saved.
162
+ */
163
+ public function on_acf_save_post( $post_id ) {
164
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
165
+ return;
166
+ }
167
+
168
+ // Don't act on post revision.
169
+ if ( wp_is_post_revision( $post_id ) ) {
170
+ return;
171
+ }
172
+
173
+ /*
174
+ Meta values look like
175
+ [product_images_0_image] => 625
176
+ [_product_images_0_image] => field_59a091044812e
177
+ [product_images_0_image_caption] => Image row yes
178
+ [_product_images_0_image_caption] => field_59a0910f4812f
179
+ [product_images_0_image_related_0_related_name] => Related one
180
+ [_product_images_0_image_related_0_related_name] => field_59aaedd43ae11
181
+ [product_images_0_image_related_0_related_item_post] =>
182
+ [_product_images_0_image_related_0_related_item_post] => field_59aaede43ae12
183
+ [product_images_0_image_related_1_related_name] => Another related
184
+ [_product_images_0_image_related_1_related_name] => field_59aaedd43ae11
185
+ [product_images_0_image_related_1_related_item_post] =>
186
+ [_product_images_0_image_related_1_related_item_post] => field_59aaede43ae12
187
+ [product_images_0_image_related] => 2
188
+ [_product_images_0_image_related] => field_59aaedbc3ae10
189
+ [product_images_1_image] => 574
190
+ */
191
+ $prev_post_meta = $this->oldPostData['prev_post_meta'];
192
+ $new_post_meta = get_post_custom( $post_id );
193
+ $new_post_meta = array_map( 'reset', $new_post_meta );
194
+
195
+ // New and old post meta can contain different amount of keys,
196
+ // join them so we have the name of all post meta thaf have been added, removed, or modified.
197
+ $new_and_old_post_meta = array_merge( $prev_post_meta, $new_post_meta );
198
+ ksort( $new_and_old_post_meta, SORT_REGULAR );
199
+
200
+ // array1 - The array to compare from
201
+ // array2 - An array to compare against
202
+ // Returns an array containing all the values from array1 that are not present in any of the other arrays.
203
+ // Keep only ACF fields in prev and new post meta.
204
+ $prev_post_meta = $this->keep_only_acf_stuff_in_array( $prev_post_meta, $new_and_old_post_meta );
205
+ $new_post_meta = $this->keep_only_acf_stuff_in_array( $new_post_meta, $new_and_old_post_meta );
206
+ $new_and_old_post_meta_acf_fields = array_merge( $prev_post_meta, $new_post_meta );
207
+
208
+ // Map field name with fieldkey so we can get field objects when needed.
209
+ // Final array have values like:
210
+ // [product_images_0_image] => field_59a091044812e
211
+ // [product_images_0_image_caption] => field_59a0910f4812f
212
+ // [product_images_0_image_related_0_related_name] => field_59aaedd43ae11.
213
+ $fieldnames_to_field_keys = array();
214
+ foreach ( $new_and_old_post_meta_acf_fields as $meta_key => $meta_value ) {
215
+ // $key is like [product_images_0_image_related_1_related_name].
216
+ // Get ACF fieldkey for that value. Will be in $new_and_old_post_meta
217
+ // as the same as key but with underscore first
218
+ $meta_key_to_look_for = "_{$meta_key}";
219
+ if ( isset( $new_and_old_post_meta[ $meta_key_to_look_for ] ) ) {
220
+ $fieldnames_to_field_keys[ $meta_key ] = $new_and_old_post_meta[ $meta_key_to_look_for ];
221
+ }
222
+ }
223
+
224
+ // Compare old with new = get only changed, not added, deleted are here.
225
+ $post_meta_diff1 = array_diff_assoc( $prev_post_meta, $new_post_meta );
226
+
227
+ // Compare new with old = get an diff with added and changed stuff.
228
+ $post_meta_diff2 = array_diff_assoc( $new_post_meta, $prev_post_meta );
229
+
230
+ // Compare keys, gets added fields.
231
+ $post_meta_added_fields = array_diff( array_keys( $post_meta_diff2 ), array_keys( $post_meta_diff1 ) );
232
+ $post_meta_added_fields = array_values( $post_meta_added_fields );
233
+
234
+ // Keys that exist in diff1 but not in diff2 = deleted.
235
+ $post_meta_removed_fields = array_diff_assoc( array_keys( $post_meta_diff1 ), array_keys( $post_meta_diff2 ) );
236
+ $post_meta_removed_fields = array_values( $post_meta_removed_fields );
237
+
238
+ $post_meta_changed_fields = array_keys( $post_meta_diff1 );
239
+
240
+ /*
241
+ * value is changed: added to both diff and diff2
242
+ * value is added, like in repeater: added to diff2 (not to diff)
243
+ * $diff3: contains only added things.
244
+ * Compare old and new values
245
+ * Loop all keys in $new_and_old_post_meta
246
+ * But act only on those whose keys begins with "_" and where the value begins with "field_" and ends with alphanum.
247
+ */
248
+
249
+ /*
250
+ * We have the diff, now add it to the context
251
+ * This is called after Simple History already has added its row
252
+ * So... we must add to the context late somehow
253
+ * Get the latest inserted row from the SimplePostLogger, check if that postID is
254
+ * same as the
255
+ */
256
+ $post_logger = $this->simpleHistory->getInstantiatedLoggerBySlug( 'SimplePostLogger' );
257
+
258
+ // Save ACF diff if detected post here is same as the last one used in Postlogger.
259
+ if ( $post_id === $post_logger->lastInsertContext['post_id'] ) {
260
+ $last_insert_id = $post_logger->lastInsertID;
261
+
262
+ // Append new info to the context of history item with id $post_logger->lastInsertID.
263
+ $acf_context = array();
264
+ $acf_context = $this->add_acf_context( $acf_context, 'added', $post_meta_added_fields, $prev_post_meta, $new_post_meta, $fieldnames_to_field_keys );
265
+ $acf_context = $this->add_acf_context( $acf_context, 'changed', $post_meta_changed_fields, $prev_post_meta, $new_post_meta, $fieldnames_to_field_keys );
266
+ $acf_context = $this->add_acf_context( $acf_context, 'removed', $post_meta_removed_fields, $prev_post_meta, $new_post_meta, $fieldnames_to_field_keys );
267
+
268
+ $post_logger->append_context( $last_insert_id, $acf_context );
269
+
270
+ // Prev and new post meta for testing.
271
+ /*
272
+ $post_logger->append_context(
273
+ $last_insert_id,
274
+ array(
275
+ 'prev_post_meta' => $prev_post_meta,
276
+ )
277
+ );
278
+ $post_logger->append_context(
279
+ $last_insert_id,
280
+ array(
281
+ 'new_post_meta' => $new_post_meta,
282
+ )
283
+ );
284
+ */
285
+ } // End if().
286
+ }
287
+
288
+ /**
289
+ * Add ACF context for added, removed, or changed fields.
290
+ *
291
+ * @param array $context Context.
292
+ * @param string $modify_type Type. added | removed | changed.
293
+ * @param array $relevant_acf_fields Fields.
294
+ * @param array $prev_post_meta Prev meta.
295
+ * @param array $new_post_meta New meta.
296
+ * @param array $fieldnames_to_field_keys Fieldnames to field keys mapping.
297
+ * @return array Modified context.
298
+ */
299
+ public function add_acf_context( $context = array(), $modify_type = '', $relevant_acf_fields = array(), $prev_post_meta, $new_post_meta, $fieldnames_to_field_keys ) {
300
+ if ( ! is_array( $context ) || empty( $modify_type ) || empty( $relevant_acf_fields ) ) {
301
+ return $context;
302
+ }
303
+
304
+ $loopnum = 0;
305
+ foreach ( $relevant_acf_fields as $field_slug ) {
306
+ /*
307
+ Store just the names to begin with
308
+ acf_field_added_0 = url.
309
+ acf_field_added_1 = first_name.
310
+
311
+ If field slug contains a number, like in "product_images_2_image"
312
+ that probably means that that field is a repeater with name "product_images"
313
+ with a sub field called "image" and that the image is the 2:nd among it's selected sub fields.
314
+
315
+ Example of how fields can look:
316
+ acf_field_added_0 product_images_2_image
317
+ acf_field_added_1 product_images_2_image_caption
318
+ acf_field_added_2 product_images_2_image_related
319
+ acf_field_changed_0 my_field_in_acf
320
+ acf_field_changed_1 product_images
321
+ acf_field_changed_2 price
322
+ acf_field_changed_3 description
323
+ */
324
+ $context_key = "acf_field_{$modify_type}_{$loopnum}";
325
+ $context[ "{$context_key}/slug" ] = $field_slug;
326
+
327
+ /*
328
+ * Try to get som extra info, like display name and type for this field.
329
+ * For a nice context in the feed we want: parent field group name and type?
330
+ */
331
+ if ( isset( $fieldnames_to_field_keys[ $field_slug ] ) ) {
332
+ $field_key = $fieldnames_to_field_keys[ $field_slug ];
333
+ $context[ "{$context_key}/key" ] = $field_key;
334
+
335
+ // Interesting stuff in field object:
336
+ // - Label = the human readable name of the field
337
+ // - Type = the type of the field
338
+ // - Parent = id of parent field post id.
339
+ $field_object = get_field_object( $field_key );
340
+ if ( is_array( $field_object ) ) {
341
+ $context[ "{$context_key}/label" ] = $field_object['label'];
342
+ if ( ! empty( $field_object['type'] ) ) {
343
+ $context[ "{$context_key}/type" ] = $field_object['type'];
344
+ }
345
+
346
+ // If no parent just continue to next field.
347
+ if ( empty( $field_object['parent'] ) ) {
348
+ continue;
349
+ }
350
+
351
+ // We have at least one parent, get them all, including the field group
352
+ // $context[ "{$context_key}/field_parent_object" ] = $parent_field;
353
+ $field_parents = array();
354
+ $field_field_group = null;
355
+
356
+ // Begin with the direct parent.
357
+ $parent_field = $field_object;
358
+
359
+ while ( ! empty( $parent_field['parent'] ) ) {
360
+ // acf-field | acf-field-group.
361
+ $parent_field_post_type = get_post_type( $parent_field['parent'] );
362
+
363
+ if ( false === $parent_field_post_type ) {
364
+ break;
365
+ }
366
+
367
+ if ( 'acf-field' === $parent_field_post_type ) {
368
+ $parent_field = _acf_get_field_by_id( $parent_field['parent'] );
369
+ } elseif ( 'acf-field-group' === $parent_field_post_type ) {
370
+ $parent_field = acf_get_field_group( $parent_field['parent'] );
371
+ } else {
372
+ // Unknown post type.
373
+ break;
374
+ }
375
+
376
+ if ( false === $parent_field ) {
377
+ break;
378
+ }
379
+
380
+ if ( 'acf-field' === $parent_field_post_type ) {
381
+ $field_parents[] = $parent_field;
382
+ } elseif ( 'acf-field-group' === $parent_field_post_type ) {
383
+ $field_field_group = $parent_field;
384
+ } // End if().
385
+ } // End while().
386
+
387
+ $field_parents = array_reverse( $field_parents );
388
+
389
+ // Array with info about each parent.
390
+ $arr_field_path = array();
391
+
392
+ if ( ! empty( $field_field_group['title'] ) ) {
393
+ $arr_field_path[] = array(
394
+ 'name' => $field_field_group['title'],
395
+ 'type' => 'field_group',
396
+ );
397
+ }
398
+
399
+ foreach ( $field_parents as $one_field_parent ) {
400
+ $arr_field_path[] = array(
401
+ 'name' => $one_field_parent['label'],
402
+ 'type' => 'field',
403
+ 'field_type' => $one_field_parent['type'],
404
+ );
405
+ }
406
+
407
+ if ( ! empty( $arr_field_path ) ) {
408
+ $path_loop_num = 0;
409
+ foreach ( $arr_field_path as $one_field_path ) {
410
+ $context[ "{$context_key}/path_{$path_loop_num}/name" ] = $one_field_path['name'];
411
+ $context[ "{$context_key}/path_{$path_loop_num}/type" ] = $one_field_path['type'];
412
+ if ( ! empty( $one_field_path['field_type'] ) ) {
413
+ $context[ "{$context_key}/path_{$path_loop_num}/field_type" ] = $one_field_path['field_type'];
414
+ }
415
+ $path_loop_num++;
416
+ }
417
+ }
418
+
419
+ // Add value of fields if they are not part of
420
+ // repeatable or flexible fields or similar.
421
+ // error_log( "Final parents" . print_r( $field_parents, 1 ) );
422
+ // error_log( "Final field group" . print_r( $field_field_group['title'], 1 ) );
423
+ // error_log( "context" . print_r( $context, 1 ) );
424
+ } // End if().
425
+ } // End if().
426
+
427
+ $loopnum++;
428
+ } // End foreach().
429
+
430
+ // error_log( "---------------------------" );
431
+ // error_log( "field_path_string: $field_path_string");
432
+ // error_log( "context" . print_r( $context, 1 ) );
433
+ return $context;
434
+ }
435
+
436
+ /**
437
+ * Clean array and keep only ACF related things.
438
+ *
439
+ * Remove
440
+ * - underscore fields
441
+ * - fields with value field_*
442
+ *
443
+ * Keep
444
+ * - vals that are acf
445
+ *
446
+ * @param array $arr Array.
447
+ * @param array $all_fields Array fields.
448
+ */
449
+ public function keep_only_acf_stuff_in_array( $arr, $all_fields ) {
450
+ $new_arr = array();
451
+
452
+ foreach ( $arr as $key => $val ) {
453
+
454
+ // Don't keep keys that begin with underscore "_".
455
+ if ( strpos( $key, '_' ) === 0 ) {
456
+ continue;
457
+ }
458
+
459
+ // Don't keep keys that begin with "field_".
460
+ if ( strpos( $val, 'field_' ) === 0 ) {
461
+ continue;
462
+ }
463
+
464
+ // Don't keep fields that does not have a corresponding _field value.
465
+ // Each key has both the name, for example 'color' and another
466
+ // key called '_color'. We check that the underscore version exists
467
+ // and contains 'field_'. After this check only ACF fields should exist
468
+ // in the array..
469
+ if ( ! isset( $all_fields[ "_{$key}" ] ) ) {
470
+ continue;
471
+ }
472
+
473
+ if ( strpos( $all_fields[ "_{$key}" ], 'field_' ) !== 0 ) {
474
+ continue;
475
+ }
476
+
477
+ $new_arr[ $key ] = $val;
478
+ }
479
+
480
+ return $new_arr;
481
+ }
482
+
483
+ /**
484
+ * Store prev post meta when post is saved.
485
+ * Stores data in $this->oldPostData.
486
+ */
487
+ public function on_admin_action_editpost() {
488
+ $post_ID = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0;
489
+
490
+ if ( ! $post_ID ) {
491
+ return;
492
+ }
493
+
494
+ $prev_post = get_post( $post_ID );
495
+
496
+ if ( is_wp_error( $prev_post ) ) {
497
+ return;
498
+ }
499
+
500
+ $post_meta = get_post_custom( $post_ID );
501
+
502
+ // Meta is array of arrays, get first value of each array value.
503
+ $post_meta = array_map( 'reset', $post_meta );
504
+
505
+ $this->oldPostData['prev_post_meta'] = $post_meta;
506
+ }
507
+
508
+ /**
509
+ * Called from PostLogger and its diff table output using filter 'simple_history/post_logger/post_updated/diff_table_output'.
510
+ * Diff table is generated only for post type 'acf-field-group'.
511
+ *
512
+ * @param string $diff_table_output
513
+ * @param array $context
514
+ * @return string
515
+ */
516
+ public function on_diff_table_output_field_group( $diff_table_output, $context ) {
517
+ $post_type = !empty($context['post_type']) ? $context['post_type'] : false;
518
+
519
+ // Bail if not ACF Field Group.
520
+ if ($post_type !== 'acf-field-group') {
521
+ return '';
522
+ }
523
+
524
+ // Field group fields to check for and output if found
525
+ $arrKeys = array(
526
+ 'instruction_placement' => array(
527
+ 'name' => _x('Instruction placement', 'Logger: Plugin ACF', 'simple-history'),
528
+ ),
529
+ 'label_placement' => array(
530
+ 'name' => _x('Label placement', 'Logger: Plugin ACF', 'simple-history'),
531
+ ),
532
+ 'description' => array(
533
+ 'name' => _x('Description', 'Logger: Plugin ACF', 'simple-history'),
534
+ ),
535
+ 'menu_order' => array(
536
+ 'name' => _x('Menu order', 'Logger: Plugin ACF', 'simple-history'),
537
+ ),
538
+ 'position' => array(
539
+ 'name' => _x('Position', 'Logger: Plugin ACF', 'simple-history'),
540
+ ),
541
+ 'active' => array(
542
+ 'name' => _x('Active', 'Logger: Plugin ACF', 'simple-history'),
543
+ ),
544
+ 'style' => array(
545
+ 'name' => _x('Style', 'Logger: Plugin ACF', 'simple-history'),
546
+ ),
547
+ );
548
+
549
+ foreach ( $arrKeys as $acfKey => $acfVals ) {
550
+ if ( isset( $context[ "acf_new_$acfKey" ] ) && isset( $context[ "acf_prev_$acfKey" ] ) ) {
551
+ $diff_table_output .= sprintf(
552
+ '<tr>
553
+ <td>%1$s</td>
554
+ <td>
555
+ <ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%2$s</ins>
556
+ <del class="SimpleHistoryLogitem__keyValueTable__removedThing">%3$s</del>
557
+ </td>
558
+ </tr>',
559
+ $acfVals['name'],
560
+ esc_html( $context[ "acf_new_$acfKey" ] ),
561
+ esc_html( $context[ "acf_prev_$acfKey" ] )
562
+ );
563
+ }
564
+ }
565
+
566
+ // If only acf_hide_on_screen_removed exists nothing is outputed.
567
+ $acf_hide_on_screen_added = empty( $context['acf_hide_on_screen_added'] ) ? null : $context['acf_hide_on_screen_added'];
568
+ $acf_hide_on_screen_removed = empty( $context['acf_hide_on_screen_removed'] ) ? null : $context['acf_hide_on_screen_removed'];
569
+
570
+ if ( $acf_hide_on_screen_added || $acf_hide_on_screen_removed ) {
571
+ $strCheckedHideOnScreen = '';
572
+ $strUncheckedHideOnScreen = '';
573
+
574
+ if ( $acf_hide_on_screen_added ) {
575
+ $strCheckedHideOnScreen = sprintf(
576
+ '%1$s %2$s',
577
+ _x( 'Checked', 'Logger: Plugin ACF', 'simple-history' ), // 1
578
+ esc_html( $acf_hide_on_screen_added ) // 2
579
+ );
580
+ }
581
+
582
+ if ( $acf_hide_on_screen_removed ) {
583
+ $strUncheckedHideOnScreen = sprintf(
584
+ '%1$s %2$s',
585
+ _x( 'Unchecked', 'Logger: Plugin ACF', 'simple-history' ), // 1
586
+ esc_html( $acf_hide_on_screen_removed ) // 2
587
+ );
588
+ }
589
+
590
+ $diff_table_output .= sprintf(
591
+ '<tr>
592
+ <td>%1$s</td>
593
+ <td>
594
+ %2$s
595
+ %3$s
596
+ </td>
597
+ </tr>',
598
+ _x( 'Hide on screen', 'Logger: Plugin ACF', 'simple-history' ), // 1
599
+ $strCheckedHideOnScreen, // 2
600
+ $strUncheckedHideOnScreen // 3
601
+ );
602
+ }
603
+
604
+ // Check for deleted fields.
605
+ if ( isset( $context['acf_deleted_fields_0_key'] ) ) {
606
+ // 1 or more deleted fields exist in context.
607
+ $loopnum = 0;
608
+ $strDeletedFields = '';
609
+
610
+ while ( isset( $context[ "acf_deleted_fields_{$loopnum}_key" ] ) ) {
611
+ $strDeletedFields .= sprintf(
612
+ '%1$s (%3$s), ',
613
+ esc_html( $context[ "acf_deleted_fields_{$loopnum}_label" ] ),
614
+ esc_html( $context[ "acf_deleted_fields_{$loopnum}_name" ] ),
615
+ esc_html( $context[ "acf_deleted_fields_{$loopnum}_type" ] )
616
+ );
617
+
618
+ $loopnum++;
619
+ }
620
+
621
+ $strDeletedFields = trim( $strDeletedFields, ', ' );
622
+
623
+ $diff_table_output .= sprintf(
624
+ '<tr>
625
+ <td>%1$s</td>
626
+ <td>%2$s</td>
627
+ </tr>',
628
+ _nx( 'Deleted field', 'Deleted fields', $loopnum, 'Logger: Plugin ACF', 'simple-history' ), // 1
629
+ $strDeletedFields
630
+ );
631
+ } // if deleted fields
632
+
633
+ // Check for added fields
634
+ if ( isset( $context['acf_added_fields_0_key'] ) ) {
635
+ // 1 or more deleted fields exist in context
636
+ $loopnum = 0;
637
+ $strAddedFields = '';
638
+
639
+ while ( isset( $context[ "acf_added_fields_{$loopnum}_key" ] ) ) {
640
+ $strAddedFields .= sprintf(
641
+ '%1$s (%3$s), ',
642
+ esc_html( $context[ "acf_added_fields_{$loopnum}_label" ] ), // 1
643
+ esc_html( $context[ "acf_added_fields_{$loopnum}_name" ] ), // 2
644
+ esc_html( $context[ "acf_added_fields_{$loopnum}_type" ] ) // 3
645
+ );
646
+
647
+ $loopnum++;
648
+ }
649
+
650
+ $strAddedFields = trim( $strAddedFields, ', ' );
651
+
652
+ $diff_table_output .= sprintf(
653
+ '<tr>
654
+ <td>%1$s</td>
655
+ <td>%2$s</td>
656
+ </tr>',
657
+ _nx( 'Added field', 'Added fields', $loopnum, 'Logger: Plugin ACF', 'simple-history' ), // 1
658
+ $strAddedFields
659
+ );
660
+ } // if deleted fields
661
+
662
+ // Check for modified fields
663
+ if ( isset( $context['acf_modified_fields_0_ID_prev'] ) ) {
664
+ // 1 or more modifiedfields exist in context
665
+ $loopnum = 0;
666
+ $strModifiedFields = '';
667
+ $arrAddedFieldsKeysToCheck = array(
668
+ 'name' => array(
669
+ 'name' => _x('Name: ', 'Logger: Plugin ACF', 'simple-history'),
670
+ ),
671
+ 'parent' => array(
672
+ 'name' => _x('Parent: ', 'Logger: Plugin ACF', 'simple-history'),
673
+ ),
674
+ 'key' => array(
675
+ 'name' => _x('Key: ', 'Logger: Plugin ACF', 'simple-history'),
676
+ ),
677
+ 'label' => array(
678
+ 'name' => _x('Label: ', 'Logger: Plugin ACF', 'simple-history'),
679
+ ),
680
+ 'type' => array(
681
+ 'name' => _x('Type: ', 'Logger: Plugin ACF', 'simple-history'),
682
+ ),
683
+ );
684
+
685
+ while ( isset( $context[ "acf_modified_fields_{$loopnum}_name_prev" ] ) ) {
686
+ // One modified field, with one or more changed things
687
+ $strOneModifiedField = '';
688
+
689
+ // Add the field name manually, if it is not among the changed field,
690
+ // or we don't know what field the other changed values belongs to.
691
+ /*
692
+ if (empty($context["acf_modified_fields_{$loopnum}_name_new"])) {
693
+ $strOneModifiedField .= sprintf(
694
+ _x('Name: %1$s', 'Logger: Plugin ACF', 'simple-history'), // 1
695
+ esc_html($context["acf_modified_fields_{$loopnum}_name_prev"]) // 2
696
+ );
697
+ }
698
+ */
699
+
700
+ // Add the label name manually, if it is not among the changed field,
701
+ // or we don't know what field the other changed values belongs to.
702
+ if ( empty( $context[ "acf_modified_fields_{$loopnum}_label_new" ] ) ) {
703
+ $strOneModifiedField .= sprintf(
704
+ _x( 'Label: %1$s', 'Logger: Plugin ACF', 'simple-history' ), // 1
705
+ esc_html( $context[ "acf_modified_fields_{$loopnum}_label_prev" ] ) // 2
706
+ );
707
+ }
708
+
709
+ // Check for other keys changed for this field
710
+ foreach ( $arrAddedFieldsKeysToCheck as $oneAddedFieldKeyToCheck => $oneAddedFieldKeyToCheckVals ) {
711
+ $newAndOldValsExists = isset( $context[ "acf_modified_fields_{$loopnum}_{$oneAddedFieldKeyToCheck}_new" ] ) && isset( $context[ "acf_modified_fields_{$loopnum}_{$oneAddedFieldKeyToCheck}_new" ] );
712
+ if ( $newAndOldValsExists ) {
713
+ $strOneModifiedField .= sprintf(
714
+ '
715
+ %4$s
716
+ %3$s
717
+ <ins class="SimpleHistoryLogitem__keyValueTable__addedThing">%1$s</ins>
718
+ <del class="SimpleHistoryLogitem__keyValueTable__removedThing">%2$s</del>
719
+ ',
720
+ esc_html( $context[ "acf_modified_fields_{$loopnum}_{$oneAddedFieldKeyToCheck}_new" ] ), // 1
721
+ esc_html( $context[ "acf_modified_fields_{$loopnum}_{$oneAddedFieldKeyToCheck}_prev" ] ), // 2
722
+ esc_html( $oneAddedFieldKeyToCheckVals['name'] ), // 3
723
+ empty( $strOneModifiedField ) ? '' : '<br>' // 4 new line
724
+ );
725
+ }
726
+ }
727
+
728
+ $strOneModifiedField = trim( $strOneModifiedField, ", \n\r\t" );
729
+
730
+ if ( $strOneModifiedField ) {
731
+ $strModifiedFields .= sprintf(
732
+ '<tr>
733
+ <td>%1$s</td>
734
+ <td>%2$s</td>
735
+ </tr>',
736
+ _x( 'Modified field', 'Logger: Plugin ACF', 'simple-history' ), // 1
737
+ $strOneModifiedField
738
+ );
739
+ }
740
+
741
+ $loopnum++;
742
+ }
743
+
744
+ /*
745
+ if ($strModifiedFields) {
746
+ $strModifiedFields = sprintf(
747
+ '<tr>
748
+ <td>%1$s</td>
749
+ <td>%2$s</td>
750
+ </tr>',
751
+ _nx('Modified field', 'Modified fields', $loopnum, 'Logger: Plugin ACF', 'simple-history'), // 1
752
+ $strModifiedFields
753
+ ) . $strModifiedFields;
754
+ }*/
755
+
756
+ $diff_table_output .= $strModifiedFields;
757
+ } // if deleted fields
758
+
759
+ return $diff_table_output;
760
+ }
761
+
762
+ /**
763
+ * Append ACF data to post context.
764
+ *
765
+ * Called via filter `simple_history/post_logger/post_updated/context`.
766
+ *
767
+ * @param array $context
768
+ * @param WP_Post $post
769
+ */
770
+ public function on_post_updated_context( $context, $post ) {
771
+
772
+ // Only act if this is a ACF field group that is saved
773
+ if ( $post->post_type !== 'acf-field-group' ) {
774
+ return $context;
775
+ }
776
+
777
+ // Remove some keys that we don't want,
778
+ // for example the content because that's just a json string
779
+ // in acf-field-group posts.
780
+ unset(
781
+ $context['post_prev_post_content'],
782
+ $context['post_new_post_content'],
783
+ $context['post_prev_post_name'],
784
+ $context['post_new_post_name'],
785
+ $context['post_prev_post_date'],
786
+ $context['post_new_post_date'],
787
+ $context['post_prev_post_date_gmt'],
788
+ $context['post_new_post_date_gmt']
789
+ );
790
+
791
+ $acf_data_diff = array();
792
+
793
+ // 'fieldGroup' fields to check.
794
+ $arr_field_group_keys_to_diff = array(
795
+ 'menu_order',
796
+ 'position',
797
+ 'style',
798
+ 'label_placement',
799
+ 'instruction_placement',
800
+ 'active',
801
+ 'description',
802
+ );
803
+
804
+ $fieldGroup = $this->oldAndNewFieldGroupsAndFields['fieldGroup'];
805
+
806
+ foreach ( $arr_field_group_keys_to_diff as $key ) {
807
+ if ( isset( $fieldGroup['old'][ $key ] ) && isset( $fieldGroup['new'][ $key ] ) ) {
808
+ $acf_data_diff = $this->add_diff( $acf_data_diff, $key, (string) $fieldGroup['old'][ $key ], (string) $fieldGroup['new'][ $key ] );
809
+ }
810
+ }
811
+
812
+ foreach ( $acf_data_diff as $diff_key => $diff_values ) {
813
+ $context[ "acf_prev_{$diff_key}" ] = $diff_values['old'];
814
+ $context[ "acf_new_{$diff_key}" ] = $diff_values['new'];
815
+ }
816
+
817
+ // Add checked or uncheckd hide on screen-items to context
818
+ $arrhHideOnScreenAdded = array();
819
+ $arrHideOnScreenRemoved = array();
820
+
821
+ $fieldGroup['new']['hide_on_screen'] = isset( $fieldGroup['new']['hide_on_screen'] ) && is_array( $fieldGroup['new']['hide_on_screen'] ) ? $fieldGroup['new']['hide_on_screen'] : array();
822
+ $fieldGroup['old']['hide_on_screen'] = isset( $fieldGroup['old']['hide_on_screen'] ) && is_array( $fieldGroup['old']['hide_on_screen'] ) ? $fieldGroup['old']['hide_on_screen'] : array();
823
+
824
+ // dd($fieldGroup['old']['hide_on_screen'], $fieldGroup['new']['hide_on_screen']);
825
+ // Act when new or old hide_on_screen is set
826
+ if ( ! empty( $fieldGroup['new']['hide_on_screen'] ) || ! empty( $fieldGroup['old']['hide_on_screen'] ) ) {
827
+ $arrhHideOnScreenAdded = array_diff( $fieldGroup['new']['hide_on_screen'], $fieldGroup['old']['hide_on_screen'] );
828
+ $arrHideOnScreenRemoved = array_diff( $fieldGroup['old']['hide_on_screen'], $fieldGroup['new']['hide_on_screen'] );
829
+
830
+ // ddd($arrhHideOnScreenAdded, $arrHideOnScreenRemoved);
831
+ if ( $arrhHideOnScreenAdded ) {
832
+ $context['acf_hide_on_screen_added'] = implode( ',', $arrhHideOnScreenAdded );
833
+ }
834
+
835
+ if ( $arrHideOnScreenRemoved ) {
836
+ $context['acf_hide_on_screen_removed'] = implode( ',', $arrHideOnScreenRemoved );
837
+ }
838
+ }
839
+
840
+ // ddd($context, $arrhHideOnScreenAdded, $arrHideOnScreenRemoved);
841
+ // Add removed fields to context
842
+ if ( ! empty( $this->oldAndNewFieldGroupsAndFields['deletedFields'] ) && is_array( $this->oldAndNewFieldGroupsAndFields['deletedFields'] ) ) {
843
+ $loopnum = 0;
844
+ foreach ( $this->oldAndNewFieldGroupsAndFields['deletedFields'] as $oneDeletedField ) {
845
+ $context[ "acf_deleted_fields_{$loopnum}_key" ] = $oneDeletedField['key'];
846
+ $context[ "acf_deleted_fields_{$loopnum}_name" ] = $oneDeletedField['name'];
847
+ $context[ "acf_deleted_fields_{$loopnum}_label" ] = $oneDeletedField['label'];
848
+ $context[ "acf_deleted_fields_{$loopnum}_type" ] = $oneDeletedField['type'];
849
+ $loopnum++;
850
+ }
851
+ }
852
+
853
+ // Add added fields to context
854
+ if ( ! empty( $this->oldAndNewFieldGroupsAndFields['addedFields'] ) && is_array( $this->oldAndNewFieldGroupsAndFields['addedFields'] ) ) {
855
+ $loopnum = 0;
856
+
857
+ foreach ( $this->oldAndNewFieldGroupsAndFields['addedFields'] as $oneAddedField ) {
858
+ // Id not available here, wold be nice to have
859
+ // $context["acf_added_fields_{$loopnum}_ID"] = $oneAddedField['ID'];
860
+ $context[ "acf_added_fields_{$loopnum}_key" ] = $oneAddedField['key'];
861
+ $context[ "acf_added_fields_{$loopnum}_name" ] = $oneAddedField['name'];
862
+ $context[ "acf_added_fields_{$loopnum}_label" ] = $oneAddedField['label'];
863
+ $context[ "acf_added_fields_{$loopnum}_type" ] = $oneAddedField['type'];
864
+ $loopnum++;
865
+ }
866
+ }
867
+
868
+ // Add modified fields to context
869
+ // dd('on_post_updated_context', $context, $this->oldAndNewFieldGroupsAndFields);
870
+ if ( ! empty( $this->oldAndNewFieldGroupsAndFields['modifiedFields']['old'] ) && ! empty( $this->oldAndNewFieldGroupsAndFields['modifiedFields']['new'] ) ) {
871
+ $modifiedFields = $this->oldAndNewFieldGroupsAndFields['modifiedFields'];
872
+
873
+ $arrAddedFieldsKeysToAdd = array(
874
+ 'parent',
875
+ 'key',
876
+ 'label',
877
+ 'name',
878
+ 'type',
879
+ );
880
+
881
+ $loopnum = 0;
882
+
883
+ foreach ( $modifiedFields['old'] as $modifiedFieldId => $modifiedFieldValues ) {
884
+ // Both old and new values mest exist
885
+ if ( empty( $modifiedFields['new'][ $modifiedFieldId ] ) ) {
886
+ continue;
887
+ }
888
+
889
+ // Always add ID, name, and lavel
890
+ $context[ "acf_modified_fields_{$loopnum}_ID_prev" ] = $modifiedFields['old'][ $modifiedFieldId ]['ID'];
891
+ $context[ "acf_modified_fields_{$loopnum}_name_prev" ] = $modifiedFields['old'][ $modifiedFieldId ]['name'];
892
+ $context[ "acf_modified_fields_{$loopnum}_label_prev" ] = $modifiedFields['old'][ $modifiedFieldId ]['label'];
893
+
894
+ foreach ( $arrAddedFieldsKeysToAdd as $oneKeyToAdd ) {
895
+ // dd($modifiedFields);
896
+ // Only add to context if modified
897
+ if ( $modifiedFields['new'][ $modifiedFieldId ][ $oneKeyToAdd ] != $modifiedFields['old'][ $modifiedFieldId ][ $oneKeyToAdd ] ) {
898
+ $context[ "acf_modified_fields_{$loopnum}_{$oneKeyToAdd}_prev" ] = $modifiedFields['old'][ $modifiedFieldId ][ $oneKeyToAdd ];
899
+ $context[ "acf_modified_fields_{$loopnum}_{$oneKeyToAdd}_new" ] = $modifiedFields['new'][ $modifiedFieldId ][ $oneKeyToAdd ];
900
+ }
901
+ }
902
+
903
+ $loopnum++;
904
+ }
905
+ }
906
+
907
+ return $context;
908
+ }
909
+
910
+ public function add_diff( $post_data_diff, $key, $old_value, $new_value ) {
911
+ if ( $old_value != $new_value ) {
912
+ $post_data_diff[ $key ] = array(
913
+ 'old' => $old_value,
914
+ 'new' => $new_value,
915
+ );
916
+ }
917
+
918
+ return $post_data_diff;
919
+ }
920
+
921
+ /**
922
+ * Store a version of the field group as it was before the save
923
+ * Called before field group post/values is added to db
924
+ */
925
+ public function on_wp_insert_post_data( $data, $postarr ) {
926
+
927
+ // Only do this if ACF field group is being saved
928
+ if ( $postarr['post_type'] !== 'acf-field-group' ) {
929
+ return $data;
930
+ }
931
+
932
+ if ( empty( $postarr['ID'] ) ) {
933
+ return $data;
934
+ }
935
+
936
+ $this->oldAndNewFieldGroupsAndFields['fieldGroup']['old'] = acf_get_field_group( $postarr['ID'] );
937
+
938
+ $this->oldAndNewFieldGroupsAndFields['fieldGroup']['new'] = acf_get_valid_field_group( $_POST['acf_field_group'] );
939
+
940
+ return $data;
941
+ }
942
+
943
+ /**
944
+ * ACF field group is saved
945
+ * Called before ACF calls its save_post filter
946
+ * Here we save the new fields values and also get the old values so we can compare
947
+ */
948
+ public function on_transition_post_status( $new_status, $old_status, $post ) {
949
+ static $isCalled = false;
950
+
951
+ if ( $isCalled ) {
952
+ return;
953
+ }
954
+
955
+ $isCalled = true;
956
+
957
+ $post_id = $post->ID;
958
+
959
+ // do not act if this is an auto save routine
960
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
961
+ return;
962
+ }
963
+
964
+ // bail early if not acf-field-group
965
+ if ( $post->post_type !== 'acf-field-group' ) {
966
+ return;
967
+ }
968
+
969
+ // only save once! WordPress save's a revision as well.
970
+ if ( wp_is_post_revision( $post_id ) ) {
971
+ return;
972
+ }
973
+
974
+ // Store info about fields that are going to be deleted
975
+ if ( ! empty( $_POST['_acf_delete_fields'] ) ) {
976
+ $deletedFieldsIDs = explode( '|', $_POST['_acf_delete_fields'] );
977
+ $deletedFieldsIDs = array_map( 'intval', $deletedFieldsIDs );
978
+
979
+ foreach ( $deletedFieldsIDs as $id ) {
980
+ if ( ! $id ) {
981
+ continue;
982
+ }
983
+
984
+ $field_info = acf_get_field( $id );
985
+
986
+ if ( ! $field_info ) {
987
+ continue;
988
+ }
989
+
990
+ $this->oldAndNewFieldGroupsAndFields['deletedFields'][ $id ] = $field_info;
991
+ }
992
+ }
993
+
994
+ // Store info about added or modified fields
995
+ if ( ! empty( $_POST['acf_fields'] ) && is_array( $_POST['acf_fields'] ) ) {
996
+ foreach ( $_POST['acf_fields'] as $oneFieldAddedOrUpdated ) {
997
+ if ( empty( $oneFieldAddedOrUpdated['ID'] ) ) {
998
+ // New fields have no id
999
+ // 'ID' => string(0) ""
1000
+ $this->oldAndNewFieldGroupsAndFields['addedFields'][] = $oneFieldAddedOrUpdated;
1001
+ } else {
1002
+ // Existing fields have an id
1003
+ // 'ID' => string(3) "383"
1004
+ $this->oldAndNewFieldGroupsAndFields['modifiedFields']['old'][ $oneFieldAddedOrUpdated['ID'] ] = acf_get_field( $oneFieldAddedOrUpdated['ID'] );
1005
+
1006
+ $this->oldAndNewFieldGroupsAndFields['modifiedFields']['new'][ $oneFieldAddedOrUpdated['ID'] ] = $oneFieldAddedOrUpdated;
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ // We don't do anything else here, but we make the actual logging
1012
+ // in filter 'acf/update_field_group' beacuse it's safer because
1013
+ // ACF has done it's validation and it's after ACF has saved the fields,
1014
+ // so less likely that we make some critical error
1015
+ }
1016
+
1017
+
1018
+ /**
1019
+ * Add the post types that ACF uses for fields to the array of post types
1020
+ * that the default post logger should not log. If not each field will cause one
1021
+ * post update log message.
1022
+ */
1023
+ public function remove_acf_from_postlogger( $skip_posttypes ) {
1024
+ array_push(
1025
+ $skip_posttypes,
1026
+ 'acf-field'
1027
+ );
1028
+
1029
+ return $skip_posttypes;
1030
+ }
1031
+ } // Class.
1032
+ } // End if().
loggers/SimpleCategoriesLogger.php CHANGED
@@ -43,61 +43,30 @@ class SimpleCategoriesLogger extends SimpleLogger {
43
 
44
  }
45
 
46
- /*
47
- * Fires after a new term is created, and after the term cache has been cleaned.
48
- *
49
- * @since 2.3.0
50
- *
51
- * @param int $term_id Term ID.
52
- * @param int $tt_id Term taxonomy ID.
53
- * @param string $taxonomy Taxonomy slug.
54
- do_action( 'created_term', $term_id, $tt_id, $taxonomy );
55
-
56
-
57
-
58
- * Fires after a term has been updated, and the term cache has been cleaned.
59
- *
60
- * @since 2.3.0
61
- *
62
- * @param int $term_id Term ID.
63
- * @param int $tt_id Term taxonomy ID.
64
- * @param string $taxonomy Taxonomy slug.
65
- do_action( "edited_term", $term_id, $tt_id, $taxonomy );
66
-
67
- * Filter the term parent.
68
- *
69
- * Hook to this filter to see if it will cause a hierarchy loop.
70
- *
71
- * @since 3.1.0
72
- *
73
- * @param int $parent ID of the parent term.
74
- * @param int $term_id Term ID.
75
- * @param string $taxonomy Taxonomy slug.
76
- * @param array $parsed_args An array of potentially altered update arguments for the given term.
77
- * @param array $args An array of update arguments for the given term.
78
- $parent = apply_filters( 'wp_update_term_parent', $args['parent'], $term_id, $taxonomy, $parsed_args, $args );
79
-
80
- */
81
-
82
  public function loaded() {
83
-
84
  add_action( 'created_term', array( $this, 'on_created_term' ), 10, 3 );
 
 
85
  add_action( 'delete_term', array( $this, 'on_delete_term' ), 10, 4 );
86
- add_action( 'wp_update_term_parent', array( $this, 'on_wp_update_term_parent' ), 10, 5 );
87
 
88
- // This action does not contain enough info to know what the term was called before the update
89
- // add_action( "edited_term", array( $this, "on_edited_term"), 10, 3 );
90
  }
91
 
92
- /*
93
  * Filter the term parent.
94
- * Only way for Simple History to get both old and new term name
 
95
  *
96
  * @param int $parent ID of the parent term.
97
  * @param int $term_id Term ID.
98
  * @param string $taxonomy Taxonomy slug.
99
  * @param array $parsed_args An array of potentially altered update arguments for the given term.
100
- * @param array $args An array of update arguments for the given term.
101
  */
102
  function on_wp_update_term_parent( $parent = null, $term_id = null, $taxonomy = null, $parsed_args = null, $term_update_args = null ) {
103
 
@@ -119,6 +88,12 @@ class SimpleCategoriesLogger extends SimpleLogger {
119
  $to_term_slug = $term_update_args['slug'];
120
  $to_term_description = $term_update_args['description'];
121
 
 
 
 
 
 
 
122
  $this->infoMessage(
123
  'edited_term',
124
  array(
@@ -131,12 +106,6 @@ class SimpleCategoriesLogger extends SimpleLogger {
131
  'to_term_taxonomy' => $to_term_taxonomy,
132
  'to_term_slug' => $to_term_slug,
133
  'to_term_description' => $to_term_description,
134
- // "term_update_args" => $term_update_args,
135
- // "term_before_edited" => $term_before_edited
136
- // "parent" => $parent,
137
- // "taxonomy" => $taxonomy,
138
- // "parsed_args" => $parsed_args,
139
- // "term_update_args" => $term_update_args
140
  )
141
  );
142
 
@@ -144,6 +113,46 @@ class SimpleCategoriesLogger extends SimpleLogger {
144
 
145
  }
146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  /*
148
  * Fires after a new term is created, and after the term cache has been cleaned.
149
  *
@@ -165,6 +174,12 @@ class SimpleCategoriesLogger extends SimpleLogger {
165
  $term_taxonomy = $term->taxonomy;
166
  $term_id = $term->term_id;
167
 
 
 
 
 
 
 
168
  $this->infoMessage(
169
  'created_term',
170
  array(
@@ -177,13 +192,13 @@ class SimpleCategoriesLogger extends SimpleLogger {
177
  }
178
 
179
 
180
- /*
181
  * Fires after a term is deleted from the database and the cache is cleaned.
182
  *
183
- * @param int $term Term ID.
184
- * @param int $tt_id Term taxonomy ID.
185
- * @param string $taxonomy Taxonomy slug.
186
- * @param mixed $deleted_term Copy of the already-deleted term, in the form specified
187
  * by the parent function. WP_Error otherwise.
188
  */
189
  function on_delete_term( $term = null, $tt_id = null, $taxonomy = null, $deleted_term = null ) {
@@ -196,32 +211,82 @@ class SimpleCategoriesLogger extends SimpleLogger {
196
  $term_taxonomy = $deleted_term->taxonomy;
197
  $term_id = $deleted_term->term_id;
198
 
 
 
 
 
 
 
199
  $this->infoMessage(
200
  'deleted_term',
201
  array(
202
  'term_id' => $term_id,
203
  'term_name' => $term_name,
204
  'term_taxonomy' => $term_taxonomy,
205
- // "deleted_term" => $deleted_term,
206
  )
207
  );
208
 
209
  }
210
 
 
 
 
 
 
 
 
 
211
 
212
- /*
213
- function on_edited_term( $term_id = null, $tt_id = null, $taxonomy ) {
 
 
 
214
 
215
- $this->debug(
216
- "on_edited_term",
 
 
 
 
 
 
 
 
 
 
217
  array(
218
- "term_id" => $term_id,
219
- "tt_id" => $tt_id,
220
- "taxonomy" => $taxonomy
221
- )
222
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
 
224
  }
225
- */
226
 
227
  }
43
 
44
  }
45
 
46
+ /**
47
+ * Called when the logger is loaded.
48
+ */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  public function loaded() {
50
+ // Fires after a new term is created, and after the term cache has been cleaned..
51
  add_action( 'created_term', array( $this, 'on_created_term' ), 10, 3 );
52
+
53
+ // Hook to this filter to see if it will cause a hierarchy loop.
54
  add_action( 'delete_term', array( $this, 'on_delete_term' ), 10, 4 );
 
55
 
56
+ // Filter the term parent.
57
+ add_action( 'wp_update_term_parent', array( $this, 'on_wp_update_term_parent' ), 10, 5 );
58
  }
59
 
60
+ /**
61
  * Filter the term parent.
62
+ * Only way for Simple History to get both old and new term name.
63
+ * For example 'edited_term' does not contain enough info to know what the term was called before the update.
64
  *
65
  * @param int $parent ID of the parent term.
66
  * @param int $term_id Term ID.
67
  * @param string $taxonomy Taxonomy slug.
68
  * @param array $parsed_args An array of potentially altered update arguments for the given term.
69
+ * @param array $term_update_args An array of update arguments for the given term.
70
  */
71
  function on_wp_update_term_parent( $parent = null, $term_id = null, $taxonomy = null, $parsed_args = null, $term_update_args = null ) {
72
 
88
  $to_term_slug = $term_update_args['slug'];
89
  $to_term_description = $term_update_args['description'];
90
 
91
+ $do_log_term = $this->ok_to_log_taxonomy( $from_term_taxonomy );
92
+
93
+ if ( ! $do_log_term ) {
94
+ return $parent;
95
+ }
96
+
97
  $this->infoMessage(
98
  'edited_term',
99
  array(
106
  'to_term_taxonomy' => $to_term_taxonomy,
107
  'to_term_slug' => $to_term_slug,
108
  'to_term_description' => $to_term_description,
 
 
 
 
 
 
109
  )
110
  );
111
 
113
 
114
  }
115
 
116
+ /**
117
+ * Check if it's ok to log a taxonomy.
118
+ * We skip some taxonomies, for example Polylang translation terms that fill the log with
119
+ * messages like 'Edited term "pll_5a3643a142c80" in taxonomy "post_translations"' otherwise.
120
+ *
121
+ * @since 2.21
122
+ * @param string $from_term_taxonomy Slug of taxonomy.
123
+ * @return bool True or false.
124
+ */
125
+ function ok_to_log_taxonomy( $from_term_taxonomy = '' ) {
126
+ if ( empty( $from_term_taxonomy ) ) {
127
+ return false;
128
+ }
129
+
130
+ $skip_taxonomies = $this->get_skip_taxonomies();
131
+
132
+ $do_log = ! in_array( $from_term_taxonomy, $skip_taxonomies, true );
133
+
134
+ return $do_log;
135
+ }
136
+
137
+ /**
138
+ * Get taxonomies to skip.
139
+ *
140
+ * @since 2.21
141
+ * @return array Array with taxonomies.
142
+ */
143
+ function get_skip_taxonomies() {
144
+
145
+ $taxonomies_to_skip = array(
146
+ // Polylang taxonomies used to store translation mappings.
147
+ 'post_translations',
148
+ 'term_translations',
149
+ );
150
+
151
+ $taxonomies_to_skip = apply_filters( 'simple_history/categories_logger/skip_taxonomies', $taxonomies_to_skip );
152
+
153
+ return $taxonomies_to_skip;
154
+ }
155
+
156
  /*
157
  * Fires after a new term is created, and after the term cache has been cleaned.
158
  *
174
  $term_taxonomy = $term->taxonomy;
175
  $term_id = $term->term_id;
176
 
177
+ $do_log_term = $this->ok_to_log_taxonomy( $term_taxonomy );
178
+
179
+ if ( ! $do_log_term ) {
180
+ return;
181
+ }
182
+
183
  $this->infoMessage(
184
  'created_term',
185
  array(
192
  }
193
 
194
 
195
+ /**
196
  * Fires after a term is deleted from the database and the cache is cleaned.
197
  *
198
+ * @param int $term Term ID.
199
+ * @param int $tt_id Term taxonomy ID.
200
+ * @param string $taxonomy Taxonomy slug.
201
+ * @param mixed $deleted_term Copy of the already-deleted term, in the form specified
202
  * by the parent function. WP_Error otherwise.
203
  */
204
  function on_delete_term( $term = null, $tt_id = null, $taxonomy = null, $deleted_term = null ) {
211
  $term_taxonomy = $deleted_term->taxonomy;
212
  $term_id = $deleted_term->term_id;
213
 
214
+ $do_log_term = $this->ok_to_log_taxonomy( $term_taxonomy );
215
+
216
+ if ( ! $do_log_term ) {
217
+ return;
218
+ }
219
+
220
  $this->infoMessage(
221
  'deleted_term',
222
  array(
223
  'term_id' => $term_id,
224
  'term_name' => $term_name,
225
  'term_taxonomy' => $term_taxonomy,
 
226
  )
227
  );
228
 
229
  }
230
 
231
+ /**
232
+ * Modify plain output to include link to term and taxonomy.
233
+ *
234
+ * @param array $row Row data.
235
+ */
236
+ public function getLogRowPlainTextOutput( $row ) {
237
+ $context = $row->context;
238
+ $message_key = isset( $context['_message_key'] ) ? $context['_message_key'] : null;
239
 
240
+ // Default to original log message.
241
+ $message = $row->message;
242
+
243
+ // Get term that was created, edited, or removed.
244
+ $term_id = isset( $context['term_id'] ) ? (int) $context['term_id'] : null;
245
 
246
+ // Get taxonomy for term.
247
+ if ( 'created_term' === $message_key || 'deleted_term' === $message_key ) {
248
+ $term_taxonomy = isset( $context['term_taxonomy'] ) ? (string) $context['term_taxonomy'] : null;
249
+ } elseif ( 'edited_term' === $message_key ) {
250
+ $term_taxonomy = isset( $context['from_term_taxonomy'] ) ? (string) $context['from_term_taxonomy'] : null;
251
+ }
252
+
253
+ if ( is_wp_error( $term_object ) ) {
254
+ return $this->interpolate( $message, $context, $row );
255
+ }
256
+
257
+ $tax_edit_link = add_query_arg(
258
  array(
259
+ 'taxonomy' => $term_taxonomy,
260
+ ),
261
+ admin_url( 'term.php' )
 
262
  );
263
+ $context['tax_edit_link'] = $tax_edit_link;
264
+
265
+ $term_object = get_term( $term_id, $term_taxonomy );
266
+ $term_edit_link = get_edit_tag_link( $term_id, $term_object->taxonomy );
267
+ $context['term_edit_link'] = $term_edit_link;
268
+
269
+ if ( 'created_term' === $message_key && ! empty( $term_edit_link ) && ! empty( $tax_edit_link ) ) {
270
+ $message = _x(
271
+ 'Added term <a href="{term_edit_link}">"{term_name}"</a> in taxonomy <a href="{tax_edit_link}">"{term_taxonomy}"</a>',
272
+ 'Categories logger: detailed plain text output for created term',
273
+ 'simple-history'
274
+ );
275
+ } elseif ( 'deleted_term' === $message_key && ! empty( $tax_edit_link ) ) {
276
+ $message = _x(
277
+ 'Deleted term "{term_name}" from taxonomy <a href="{tax_edit_link}">"{term_taxonomy}"</a>',
278
+ 'Categories logger: detailed plain text output for deleted term',
279
+ 'simple-history'
280
+ );
281
+ } elseif ( 'edited_term' === $message_key && ! empty( $term_edit_link ) && ! empty( $tax_edit_link ) ) {
282
+ $message = _x(
283
+ 'Edited term <a href="{term_edit_link}">"{to_term_name}"</a> in taxonomy <a href="{tax_edit_link}">"{to_term_taxonomy}"</a>',
284
+ 'Categories logger: detailed plain text output for edited term',
285
+ 'simple-history'
286
+ );
287
+ }
288
 
289
+ return $this->interpolate( $message, $context, $row );
290
  }
 
291
 
292
  }
loggers/SimpleLogger.php CHANGED
@@ -36,10 +36,27 @@ class SimpleLogger {
36
  public $messages;
37
 
38
  /**
39
- * ID of last inserted row. Used when chaining methods.
 
 
40
  */
41
  public $lastInsertID;
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  /**
44
  * Constructor. Remember to call this as parent constructor if making a childlogger
45
  *
@@ -931,9 +948,9 @@ class SimpleLogger {
931
  /**
932
  * Logs with an arbitrary level.
933
  *
934
- * @param mixed $level The log level.
935
- * @param string $message The log message.
936
- * @param array $context The log context.
937
  * @return class SimpleLogger instance
938
  */
939
  public function log( $level = 'info', $message = '', $context = array() ) {
@@ -1049,7 +1066,7 @@ class SimpleLogger {
1049
 
1050
  // No occasions id specified, create one bases on the data array
1051
  $occasions_data = $data + $context;
1052
- // error_log(simpleHistory::json_encode($occasions_data));
1053
  // Don't include date in context data
1054
  unset( $occasions_data['date'] );
1055
 
@@ -1188,8 +1205,7 @@ class SimpleLogger {
1188
  }
1189
  }
1190
 
1191
- // Add remote addr to context
1192
- // Good to always have
1193
  if ( ! isset( $context['_server_remote_addr'] ) ) {
1194
 
1195
  $context['_server_remote_addr'] = empty( $_SERVER['REMOTE_ADDR'] ) ? '' : $_SERVER['REMOTE_ADDR'];
@@ -1232,8 +1248,7 @@ class SimpleLogger {
1232
  }
1233
  }// End if().
1234
 
1235
- // Append http referer
1236
- // Also good to always have!
1237
  if ( ! isset( $context['_server_http_referer'] ) && isset( $_SERVER['HTTP_REFERER'] ) ) {
1238
  $context['_server_http_referer'] = $_SERVER['HTTP_REFERER'];
1239
  }
@@ -1250,31 +1265,12 @@ class SimpleLogger {
1250
  $context = apply_filters( 'simple_history/log_insert_context', $context, $data, $this );
1251
  $data_parent_row = $data;
1252
 
1253
- // Insert all context values into db
1254
- foreach ( $context as $key => $value ) {
1255
-
1256
- // If value is array or object then use json_encode to store it
1257
- // if ( is_object( $value ) || is_array( $value ) ) {
1258
- // $value = simpleHistory::json_encode($value);
1259
- // }
1260
- // Any reason why the check is not the other way around?
1261
- // Everything except strings should be json_encoded
1262
- if ( ! is_string( $value ) ) {
1263
- $value = simpleHistory::json_encode( $value );
1264
- }
1265
-
1266
- $data = array(
1267
- 'history_id' => $history_inserted_id,
1268
- 'key' => $key,
1269
- 'value' => $value,
1270
- );
1271
-
1272
- $result = $wpdb->insert( $db_table_contexts, $data );
1273
-
1274
- }
1275
  }// End if().
1276
 
1277
  $this->lastInsertID = $history_inserted_id;
 
1278
 
1279
  $this->simpleHistory->get_cache_incrementor( true );
1280
 
@@ -1283,17 +1279,52 @@ class SimpleLogger {
1283
  *
1284
  * @since 2.5.1
1285
  *
1286
- * @param array $context Array with all context data to store. Modify and return this.
1287
  * @param array $data Array with data used for parent row.
1288
  * @param array $this Reference to this logger instance
1289
  */
1290
  do_action( 'simple_history/log/inserted', $context, $data_parent_row, $this );
1291
 
1292
- // Return $this so we can chain methods
1293
  return $this;
1294
 
1295
  } // log
1296
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1297
  /**
1298
  * Returns array with headers that may contain user IP
1299
  *
@@ -1318,6 +1349,8 @@ class SimpleLogger {
1318
  * Returns additional headers with ip number from context
1319
  *
1320
  * @since 2.0.29
 
 
1321
  */
1322
  function get_event_ip_number_headers( $row ) {
1323
 
@@ -1346,6 +1379,9 @@ class SimpleLogger {
1346
  /**
1347
  * Ensures an ip address is both a valid IP and does not fall within
1348
  * a private network range.
 
 
 
1349
  */
1350
  function validate_ip( $ip ) {
1351
 
@@ -1397,21 +1433,21 @@ class SimpleLogger {
1397
  class SimpleLoggerLogInitiators {
1398
 
1399
  // A wordpress user that at the log event created did exist in the wp database
1400
- // May have been deleted when the log is viewed
1401
  const WP_USER = 'wp_user';
1402
 
1403
  // Cron job run = wordpress initiated
1404
  // Email sent to customer on webshop = system/wordpress/anonymous web user
1405
- // Javascript error occured on website = anonymous web user
1406
  const WEB_USER = 'web_user';
1407
 
1408
- // WordPress core or plugins updated automatically via wp-cron
1409
  const WORDPRESS = 'wp';
1410
 
1411
- // WP CLI / terminal
1412
  const WP_CLI = 'wp_cli';
1413
 
1414
- // I dunno
1415
  const OTHER = 'other';
1416
  }
1417
 
36
  public $messages;
37
 
38
  /**
39
+ * ID of last inserted row
40
+ *
41
+ * @var int $lastInsertID Database row primary key.
42
  */
43
  public $lastInsertID;
44
 
45
+ /**
46
+ * Context of last inserted row.
47
+ *
48
+ * @var int $lastInsertContext Context used for the last insert.
49
+ * @since 2.2x
50
+ */
51
+ public $lastInsertContext;
52
+
53
+ /**
54
+ * Simple History instance.
55
+ *
56
+ * @var object $simpleHistory Simple history instance.
57
+ */
58
+ public $simpleHistory;
59
+
60
  /**
61
  * Constructor. Remember to call this as parent constructor if making a childlogger
62
  *
948
  /**
949
  * Logs with an arbitrary level.
950
  *
951
+ * @param mixed $level The log level. Default "info".
952
+ * @param string $message The log message. Default "".
953
+ * @param array $context The log context. Default empty array.
954
  * @return class SimpleLogger instance
955
  */
956
  public function log( $level = 'info', $message = '', $context = array() ) {
1066
 
1067
  // No occasions id specified, create one bases on the data array
1068
  $occasions_data = $data + $context;
1069
+
1070
  // Don't include date in context data
1071
  unset( $occasions_data['date'] );
1072
 
1205
  }
1206
  }
1207
 
1208
+ // Add remote addr to context.
 
1209
  if ( ! isset( $context['_server_remote_addr'] ) ) {
1210
 
1211
  $context['_server_remote_addr'] = empty( $_SERVER['REMOTE_ADDR'] ) ? '' : $_SERVER['REMOTE_ADDR'];
1248
  }
1249
  }// End if().
1250
 
1251
+ // Append http referer.
 
1252
  if ( ! isset( $context['_server_http_referer'] ) && isset( $_SERVER['HTTP_REFERER'] ) ) {
1253
  $context['_server_http_referer'] = $_SERVER['HTTP_REFERER'];
1254
  }
1265
  $context = apply_filters( 'simple_history/log_insert_context', $context, $data, $this );
1266
  $data_parent_row = $data;
1267
 
1268
+ // Insert all context values into db.
1269
+ $this->append_context( $history_inserted_id, $context );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1270
  }// End if().
1271
 
1272
  $this->lastInsertID = $history_inserted_id;
1273
+ $this->lastInsertContext = $context;
1274
 
1275
  $this->simpleHistory->get_cache_incrementor( true );
1276
 
1279
  *
1280
  * @since 2.5.1
1281
  *
1282
+ * @param array $context Array with all context data that was used to log event.
1283
  * @param array $data Array with data used for parent row.
1284
  * @param array $this Reference to this logger instance
1285
  */
1286
  do_action( 'simple_history/log/inserted', $context, $data_parent_row, $this );
1287
 
1288
+ // Return $this so we can chain methods.
1289
  return $this;
1290
 
1291
  } // log
1292
 
1293
+ /**
1294
+ * Append new info to the contextof history item with id $post_logger->lastInsertID..
1295
+ *
1296
+ * @param int $history_id The id of the history row to add context to.
1297
+ * @param array $context Context to append to existing context for the row.
1298
+ * @return bool True if context was added, false if not (beacuse row_id or context is empty).
1299
+ */
1300
+ public function append_context( $history_id, $context ) {
1301
+ if ( empty( $history_id ) || empty( $context ) ) {
1302
+ return false;
1303
+ }
1304
+
1305
+ global $wpdb;
1306
+
1307
+ $db_table_contexts = $wpdb->prefix . SimpleHistory::DBTABLE_CONTEXTS;
1308
+
1309
+ foreach ( $context as $key => $value ) {
1310
+
1311
+ // Everything except strings should be json_encoded, ie. arrays and objects.
1312
+ if ( ! is_string( $value ) ) {
1313
+ $value = simpleHistory::json_encode( $value );
1314
+ }
1315
+
1316
+ $data = array(
1317
+ 'history_id' => $history_id,
1318
+ 'key' => $key,
1319
+ 'value' => $value,
1320
+ );
1321
+
1322
+ $result = $wpdb->insert( $db_table_contexts, $data );
1323
+ }
1324
+
1325
+ return true;
1326
+ }
1327
+
1328
  /**
1329
  * Returns array with headers that may contain user IP
1330
  *
1349
  * Returns additional headers with ip number from context
1350
  *
1351
  * @since 2.0.29
1352
+ * @param array $row Row with info.
1353
+ * @return array Headers
1354
  */
1355
  function get_event_ip_number_headers( $row ) {
1356
 
1379
  /**
1380
  * Ensures an ip address is both a valid IP and does not fall within
1381
  * a private network range.
1382
+ *
1383
+ * @param string $ip IP number.
1384
+ * @return bool
1385
  */
1386
  function validate_ip( $ip ) {
1387
 
1433
  class SimpleLoggerLogInitiators {
1434
 
1435
  // A wordpress user that at the log event created did exist in the wp database
1436
+ // May have been deleted when the log is viewed.
1437
  const WP_USER = 'wp_user';
1438
 
1439
  // Cron job run = wordpress initiated
1440
  // Email sent to customer on webshop = system/wordpress/anonymous web user
1441
+ // Javascript error occured on website = anonymous web user.
1442
  const WEB_USER = 'web_user';
1443
 
1444
+ // WordPress core or plugins updated automatically via wp-cron.
1445
  const WORDPRESS = 'wp';
1446
 
1447
+ // WP CLI / terminal.
1448
  const WP_CLI = 'wp_cli';
1449
 
1450
+ // I dunno.
1451
  const OTHER = 'other';
1452
  }
1453
 
loggers/SimplePluginLogger.php CHANGED
@@ -3,12 +3,12 @@
3
  defined( 'ABSPATH' ) || die();
4
 
5
  /**
6
- * Logs plugin installs, updates, and deletions
7
  */
8
  class SimplePluginLogger extends SimpleLogger {
9
 
10
  /**
11
- * The logger slug. Defaulting to the class name is nice and logical I think
12
  *
13
  * @var string $slug
14
  */
@@ -28,58 +28,58 @@ class SimplePluginLogger extends SimpleLogger {
28
  *
29
  * @return array
30
  */
31
- function getInfo() {
32
 
33
  $arr_info = array(
34
- 'name' => 'Plugin Logger',
35
  'description' => 'Logs plugin installs, uninstalls and updates',
36
- 'capability' => 'activate_plugins',
37
- 'messages' => array(
38
 
39
- 'plugin_activated' => _x(
40
  'Activated plugin "{plugin_name}"',
41
  'Plugin was non-silently activated by a user',
42
  'simple-history'
43
  ),
44
 
45
- 'plugin_deactivated' => _x(
46
  'Deactivated plugin "{plugin_name}"',
47
  'Plugin was non-silently deactivated by a user',
48
  'simple-history'
49
  ),
50
 
51
- 'plugin_installed' => _x(
52
  'Installed plugin "{plugin_name}"',
53
  'Plugin was installed',
54
  'simple-history'
55
  ),
56
 
57
- 'plugin_installed_failed' => _x(
58
  'Failed to install plugin "{plugin_name}"',
59
  'Plugin failed to install',
60
  'simple-history'
61
  ),
62
 
63
- 'plugin_updated' => _x(
64
  'Updated plugin "{plugin_name}" to version {plugin_version} from {plugin_prev_version}',
65
  'Plugin was updated',
66
  'simple-history'
67
  ),
68
 
69
- 'plugin_update_failed' => _x(
70
  'Failed to update plugin "{plugin_name}"',
71
  'Plugin update failed',
72
  'simple-history'
73
  ),
74
 
75
- 'plugin_deleted' => _x(
76
  'Deleted plugin "{plugin_name}"',
77
  'Plugin files was deleted',
78
  'simple-history'
79
  ),
80
 
81
  // Bulk versions.
82
- 'plugin_bulk_updated' => _x(
83
  'Updated plugin "{plugin_name}" to {plugin_version} from {plugin_prev_version}',
84
  'Plugin was updated in bulk',
85
  'simple-history'
@@ -93,33 +93,33 @@ class SimplePluginLogger extends SimpleLogger {
93
  ),
94
 
95
  ), // Messages.
96
- 'labels' => array(
97
  'search' => array(
98
- 'label' => _x( 'Plugins', 'Plugin logger: search', 'simple-history' ),
99
  'label_all' => _x( 'All plugin activity', 'Plugin logger: search', 'simple-history' ),
100
- 'options' => array(
101
  _x( 'Activated plugins', 'Plugin logger: search', 'simple-history' ) => array(
102
- 'plugin_activated'
103
  ),
104
  _x( 'Deactivated plugins', 'Plugin logger: search', 'simple-history' ) => array(
105
  'plugin_deactivated',
106
  'plugin_disabled_because_error',
107
  ),
108
  _x( 'Installed plugins', 'Plugin logger: search', 'simple-history' ) => array(
109
- 'plugin_installed'
110
  ),
111
  _x( 'Failed plugin installs', 'Plugin logger: search', 'simple-history' ) => array(
112
- 'plugin_installed_failed'
113
  ),
114
  _x( 'Updated plugins', 'Plugin logger: search', 'simple-history' ) => array(
115
  'plugin_updated',
116
  'plugin_bulk_updated',
117
  ),
118
  _x( 'Failed plugin updates', 'Plugin logger: search', 'simple-history' ) => array(
119
- 'plugin_update_failed'
120
  ),
121
  _x( 'Deleted plugins', 'Plugin logger: search', 'simple-history' ) => array(
122
- 'plugin_deleted'
123
  ),
124
  ),
125
  ), // search array.
@@ -154,24 +154,26 @@ class SimplePluginLogger extends SimpleLogger {
154
  // this hook does not fire.
155
  add_action( 'deactivated_plugin', array( $this, 'on_deactivated_plugin' ), 10, 2 );
156
 
157
- // Fires after the upgrades has done it's thing
158
- // Check hook extra for upgrader initiator
159
  add_action( 'upgrader_process_complete', array( $this, 'on_upgrader_process_complete' ), 10, 2 );
160
 
161
- // Detect files removed
162
  add_action( 'setted_transient', array( $this, 'on_setted_transient_for_remove_files' ), 10, 2 );
163
 
164
  add_action( 'admin_action_delete-selected', array( $this, 'on_action_delete_selected' ), 10, 1 );
165
 
166
- // Ajax function to get info from GitHub repo. Used by "View plugin info"-link for plugin installs
167
  add_action( 'wp_ajax_SimplePluginLogger_GetGitHubPluginInfo', array( $this, 'ajax_GetGitHubPluginInfo' ) );
168
 
169
  // If the Github Update plugin is not installed we need to get extra fields used by it.
170
  // So need to hook filter "extra_plugin_headers" ourself.
171
- add_filter( 'extra_plugin_headers', function( $arr_headers ) {
172
- $arr_headers[] = 'GitHub Plugin URI';
173
- return $arr_headers;
174
- } );
 
 
175
 
176
  // There is no way to use a filter and detect a plugin that is disabled because it can't be found or similar error.
177
  // So we hook into gettext and look for the usage of the error that is returned when this happens.
@@ -229,7 +231,7 @@ class SimplePluginLogger extends SimpleLogger {
229
  * we hook into gettext and look for the usage of the error that is returned when this happens.
230
  *
231
  * A plugin gets deactivated when plugins.php is visited function validate_active_plugins()
232
- * return new WP_Error('plugin_not_found', __('Plugin file does not exist.'));
233
  * and if invalid plugin is found then this is outputed
234
  * printf(
235
  * /* translators: 1: plugin file 2: error message
@@ -246,7 +248,7 @@ class SimplePluginLogger extends SimpleLogger {
246
  global $pagenow;
247
 
248
  // We only act on page plugins.php.
249
- if ( ! isset( $pagenow ) || $pagenow !== 'plugins.php' ) {
250
  return $translation;
251
  }
252
 
@@ -254,7 +256,7 @@ class SimplePluginLogger extends SimpleLogger {
254
  // (Literally these, no translation)
255
  $untranslated_texts = array(
256
  // This string is called later than the above
257
- 'The plugin %1$s has been <strong>deactivated</strong> due to an error: %2$s'
258
  );
259
 
260
  if ( ! in_array( $text, $untranslated_texts ) ) {
@@ -267,28 +269,30 @@ class SimplePluginLogger extends SimpleLogger {
267
  // stuff the first time it's called (directly after the gettet for the plugin disabled-error..).
268
  $logger_instance = $this;
269
 
270
- add_filter( 'esc_html', function( $safe_text, $text ) use ( $logger_instance ) {
271
- static $is_called = false;
 
272
 
273
- if ( false === $is_called ) {
274
- $is_called = true;
275
 
276
- $deactivation_reason = array_shift( $logger_instance->latest_plugin_deactivation_because_of_error_reason );
277
 
278
- // We don't know what plugin that was that got this error and currently there does not seem to be a way to determine that.
279
- // So that's why we use such generic log messages.
280
- $logger_instance->warningMessage(
281
- 'plugin_disabled_because_error',
282
- array(
283
- '_initiator' => SimpleLoggerLogInitiators::WORDPRESS,
284
- 'plugin_slug' => $text,
285
- 'deactivation_reason' => $deactivation_reason,
286
- )
287
- );
288
- }
289
 
290
- return $safe_text;
291
- }, 10, 2 );
 
292
 
293
  return $translation;
294
 
@@ -315,18 +319,20 @@ class SimplePluginLogger extends SimpleLogger {
315
  }
316
 
317
  $repo_username = $repo_parts[3];
318
- $repo_repo = $repo_parts[4];
319
 
320
  // https://developer.github.com/v3/repos/contents/
321
  // https://api.github.com/repos/<username>/<repo>/readme
322
  $api_url = sprintf( 'https://api.github.com/repos/%1$s/%2$s/readme', urlencode( $repo_username ), urlencode( $repo_repo ) );
323
 
324
  // Get file. Use accept-header to get file as HTML instead of JSON
325
- $response = wp_remote_get( $api_url, array(
326
- 'headers' => array(
327
- 'accept' => 'application/vnd.github.VERSION.html',
328
- ),
329
- ) );
 
 
330
 
331
  $response_body = wp_remote_retrieve_body( $response );
332
 
@@ -458,28 +464,28 @@ class SimplePluginLogger extends SimpleLogger {
458
  ) {
459
 
460
  /*
461
- [checked] => Array
462
- (
463
- [0] => the-events-calendar/the-events-calendar.php
464
- )
465
- */
466
 
467
- $plugins_deleted = $_POST['checked'];
468
  $plugins_before_update = json_decode( get_option( $this->slug . '_plugin_info_before_update', false ), true );
469
 
470
  foreach ( $plugins_deleted as $plugin ) {
471
 
472
  $context = array(
473
- 'plugin' => $plugin,// plugin-name-folder/plugin-main-file.php
474
  );
475
 
476
  if ( is_array( $plugins_before_update ) && isset( $plugins_before_update[ $plugin ] ) ) {
477
- $context['plugin_name'] = $plugins_before_update[ $plugin ]['Name'];
478
- $context['plugin_title'] = $plugins_before_update[ $plugin ]['Title'];
479
  $context['plugin_description'] = $plugins_before_update[ $plugin ]['Description'];
480
- $context['plugin_author'] = $plugins_before_update[ $plugin ]['Author'];
481
- $context['plugin_version'] = $plugins_before_update[ $plugin ]['Version'];
482
- $context['plugin_url'] = $plugins_before_update[ $plugin ]['PluginURI'];
483
  }
484
 
485
  $this->infoMessage(
@@ -565,10 +571,10 @@ class SimplePluginLogger extends SimpleLogger {
565
  If an update fails then $plugin_upgrader_instance->skin->result->errors contains something like:
566
  Array
567
  (
568
- [remove_old_failed] => Array
569
- (
570
- [0] => Could not remove the old plugin.
571
- )
572
 
573
  )
574
  */
@@ -581,8 +587,8 @@ class SimplePluginLogger extends SimpleLogger {
581
  $arr_data:
582
  Array
583
  (
584
- [action] => update
585
- [type] => core
586
  )
587
 
588
 
@@ -591,8 +597,8 @@ class SimplePluginLogger extends SimpleLogger {
591
  $arr_data:
592
  Array
593
  (
594
- [type] => plugin
595
- [action] => install
596
  )
597
 
598
 
@@ -601,8 +607,8 @@ class SimplePluginLogger extends SimpleLogger {
601
  $arr_data:
602
  Array
603
  (
604
- [type] => plugin
605
- [action] => install
606
  )
607
 
608
  ## Bulk actions
@@ -626,24 +632,24 @@ class SimplePluginLogger extends SimpleLogger {
626
  if ( isset( $arr_data['action'] ) && 'install' == $arr_data['action'] && ! $plugin_upgrader_instance->bulk ) {
627
 
628
  $upgrader_skin_options = isset( $plugin_upgrader_instance->skin->options ) && is_array( $plugin_upgrader_instance->skin->options ) ? $plugin_upgrader_instance->skin->options : array();
629
- $upgrader_skin_result = isset( $plugin_upgrader_instance->skin->result ) && is_array( $plugin_upgrader_instance->skin->result ) ? $plugin_upgrader_instance->skin->result : array();
630
- $upgrader_skin_api = isset( $plugin_upgrader_instance->skin->api ) ? $plugin_upgrader_instance->skin->api : (object) array();
631
 
632
  $plugin_slug = isset( $upgrader_skin_result['destination_name'] ) ? $upgrader_skin_result['destination_name'] : '';
633
 
634
  // Upgrader contains current info
635
  $context = array(
636
- 'plugin_slug' => $plugin_slug,
637
- 'plugin_name' => isset( $upgrader_skin_api->name ) ? $upgrader_skin_api->name : '',
638
- 'plugin_version' => isset( $upgrader_skin_api->version ) ? $upgrader_skin_api->version : '',
639
- 'plugin_author' => isset( $upgrader_skin_api->author ) ? $upgrader_skin_api->author : '',
640
  'plugin_last_updated' => isset( $upgrader_skin_api->last_updated ) ? $upgrader_skin_api->last_updated : '',
641
- 'plugin_requires' => isset( $upgrader_skin_api->requires ) ? $upgrader_skin_api->requires : '',
642
- 'plugin_tested' => isset( $upgrader_skin_api->tested ) ? $upgrader_skin_api->tested : '',
643
- 'plugin_rating' => isset( $upgrader_skin_api->rating ) ? $upgrader_skin_api->rating : '',
644
- 'plugin_num_ratings' => isset( $upgrader_skin_api->num_ratings ) ? $upgrader_skin_api->num_ratings : '',
645
- 'plugin_downloaded' => isset( $upgrader_skin_api->downloaded ) ? $upgrader_skin_api->downloaded : '',
646
- 'plugin_added' => isset( $upgrader_skin_api->added ) ? $upgrader_skin_api->added : '',
647
  'plugin_source_files' => $this->simpleHistory->json_encode( $plugin_upgrader_instance->result['source_files'] ),
648
 
649
  // To debug comment out these:
@@ -674,18 +680,18 @@ class SimplePluginLogger extends SimpleLogger {
674
  /*
675
  _debug_files
676
  {
677
- "pluginzip": {
678
- "name": "WPThumb-master.zip",
679
- "type": "application\/zip",
680
- "tmp_name": "\/Applications\/MAMP\/tmp\/php\/phpnThImc",
681
- "error": 0,
682
- "size": 2394625
683
- }
684
  }
685
  */
686
 
687
  if ( isset( $_FILES['pluginzip']['name'] ) ) {
688
- $plugin_upload_name = $_FILES['pluginzip']['name'];
689
  $context['plugin_upload_name'] = $plugin_upload_name;
690
  }
691
  }
@@ -695,7 +701,7 @@ class SimplePluginLogger extends SimpleLogger {
695
  // Add errors
696
  // Errors is in original wp admin language
697
  $context['error_messages'] = $this->simpleHistory->json_encode( $plugin_upgrader_instance->skin->result->errors );
698
- $context['error_data'] = $this->simpleHistory->json_encode( $plugin_upgrader_instance->skin->result->error_data );
699
 
700
  $this->infoMessage(
701
  'plugin_installed_failed',
@@ -720,11 +726,11 @@ class SimplePluginLogger extends SimpleLogger {
720
  $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_info, true, false );
721
  }
722
 
723
- $context['plugin_name'] = isset( $plugin_data['Name'] ) ? $plugin_data['Name'] : '';
724
  $context['plugin_description'] = isset( $plugin_data['Description'] ) ? $plugin_data['Description'] : '';
725
- $context['plugin_url'] = isset( $plugin_data['PluginURI'] ) ? $plugin_data['PluginURI'] : '';
726
- $context['plugin_version'] = isset( $plugin_data['Version'] ) ? $plugin_data['Version'] : '';
727
- $context['plugin_author'] = isset( $plugin_data['AuthorName'] ) ? $plugin_data['AuthorName'] : '';
728
 
729
  // Comment out these to debug plugin installs
730
  // $context["debug_plugin_data"] = $this->simpleHistory->json_encode( $plugin_data );
@@ -757,14 +763,14 @@ class SimplePluginLogger extends SimpleLogger {
757
  $plugin_slug = dirname( $arr_data['plugin'] );
758
 
759
  $context = array(
760
- 'plugin_slug' => $plugin_slug,
761
- 'request' => $this->simpleHistory->json_encode( $_REQUEST ),
762
- 'plugin_name' => $plugin_data['Name'],
763
- 'plugin_title' => $plugin_data['Title'],
764
- 'plugin_description' => $plugin_data['Description'],
765
- 'plugin_author' => $plugin_data['Author'],
766
- 'plugin_version' => $plugin_data['Version'],
767
- 'plugin_url' => $plugin_data['PluginURI'],
768
  'plugin_source_files' => $this->simpleHistory->json_encode( $plugin_upgrader_instance->result['source_files'] ),
769
  );
770
 
@@ -813,7 +819,7 @@ class SimplePluginLogger extends SimpleLogger {
813
  // Add errors
814
  // Errors is in original wp admin language
815
  $context['error_messages'] = json_encode( $plugin_upgrader_instance->skin->result->errors );
816
- $context['error_data'] = json_encode( $plugin_upgrader_instance->skin->result->error_data );
817
 
818
  $this->infoMessage(
819
  'plugin_update_failed',
@@ -862,13 +868,13 @@ class SimplePluginLogger extends SimpleLogger {
862
  $plugin_slug = dirname( $plugin_name );
863
 
864
  $context = array(
865
- 'plugin_slug' => $plugin_slug,
866
- 'plugin_name' => $plugin_data['Name'],
867
- 'plugin_title' => $plugin_data['Title'],
868
  'plugin_description' => $plugin_data['Description'],
869
- 'plugin_author' => $plugin_data['Author'],
870
- 'plugin_version' => $plugin_data['Version'],
871
- 'plugin_url' => $plugin_data['PluginURI'],
872
  );
873
 
874
  // get url and package
@@ -954,13 +960,13 @@ class SimplePluginLogger extends SimpleLogger {
954
  $plugin_slug = dirname( $plugin_name );
955
 
956
  $context = array(
957
- 'plugin_name' => $plugin_data['Name'],
958
- 'plugin_slug' => $plugin_slug,
959
- 'plugin_title' => $plugin_data['Title'],
960
  'plugin_description' => $plugin_data['Description'],
961
- 'plugin_author' => $plugin_data['Author'],
962
- 'plugin_version' => $plugin_data['Version'],
963
- 'plugin_url' => $plugin_data['PluginURI'],
964
  );
965
 
966
  if ( ! empty( $plugin_data['GitHub Plugin URI'] ) ) {
@@ -981,13 +987,13 @@ class SimplePluginLogger extends SimpleLogger {
981
  $plugin_slug = dirname( $plugin_name );
982
 
983
  $context = array(
984
- 'plugin_name' => $plugin_data['Name'],
985
- 'plugin_slug' => $plugin_slug,
986
- 'plugin_title' => $plugin_data['Title'],
987
  'plugin_description' => $plugin_data['Description'],
988
- 'plugin_author' => $plugin_data['Author'],
989
- 'plugin_version' => $plugin_data['Version'],
990
- 'plugin_url' => $plugin_data['PluginURI'],
991
  );
992
 
993
  if ( ! empty( $plugin_data['GitHub Plugin URI'] ) ) {
@@ -1004,9 +1010,9 @@ class SimplePluginLogger extends SimpleLogger {
1004
  */
1005
  function getLogRowDetailsOutput( $row ) {
1006
 
1007
- $context = $row->context;
1008
  $message_key = $context['_message_key'];
1009
- $output = '';
1010
 
1011
  // When a plugin is installed we show a bit more information
1012
  // We do it only on install because we don't want to clutter to log,
@@ -1018,19 +1024,19 @@ class SimplePluginLogger extends SimpleLogger {
1018
 
1019
  // Description includes a link to author, remove that, i.e. all text after and including <cite>
1020
  $plugin_description = $context['plugin_description'];
1021
- $cite_pos = strpos( $plugin_description, '<cite>' );
1022
  if ( $cite_pos ) {
1023
  $plugin_description = substr( $plugin_description, 0, $cite_pos );
1024
  }
1025
 
1026
  // Keys to show
1027
  $arr_plugin_keys = array(
1028
- 'plugin_description' => _x( 'Description', 'plugin logger - detailed output', 'simple-history' ),
1029
- 'plugin_install_source' => _x( 'Source', 'plugin logger - detailed output install source', 'simple-history' ),
1030
  'plugin_install_source_file' => _x( 'Source file name', 'plugin logger - detailed output install source', 'simple-history' ),
1031
- 'plugin_version' => _x( 'Version', 'plugin logger - detailed output version', 'simple-history' ),
1032
- 'plugin_author' => _x( 'Author', 'plugin logger - detailed output author', 'simple-history' ),
1033
- 'plugin_url' => _x( 'URL', 'plugin logger - detailed output url', 'simple-history' ),
1034
  // "plugin_downloaded" => _x("Downloads", "plugin logger - detailed output downloaded", "simple-history"),
1035
  // "plugin_requires" => _x("Requires", "plugin logger - detailed output author", "simple-history"),
1036
  // "plugin_tested" => _x("Compatible up to", "plugin logger - detailed output compatible", "simple-history"),
@@ -1067,7 +1073,6 @@ class SimplePluginLogger extends SimpleLogger {
1067
  break;
1068
 
1069
  case 'plugin_install_source':
1070
-
1071
  if ( ! isset( $context[ $key ] ) ) {
1072
  continue;
1073
  }
@@ -1086,14 +1091,13 @@ class SimplePluginLogger extends SimpleLogger {
1086
  break;
1087
 
1088
  case 'plugin_install_source_file':
1089
-
1090
  if ( ! isset( $context['plugin_upload_name'] ) || ! isset( $context['plugin_install_source'] ) ) {
1091
  continue;
1092
  }
1093
 
1094
  if ( 'upload' == $context['plugin_install_source'] ) {
1095
  $plugin_upload_name = $context['plugin_upload_name'];
1096
- $desc_output = esc_html( $plugin_upload_name );
1097
  }
1098
 
1099
  break;
@@ -1169,7 +1173,7 @@ class SimplePluginLogger extends SimpleLogger {
1169
  if ( $plugin_slug && empty( $context['plugin_github_url'] ) ) {
1170
 
1171
  $link_title = esc_html_x( 'View plugin info', 'plugin logger: plugin info thickbox title', 'simple-history' );
1172
- $url = admin_url( "plugin-install.php?tab=plugin-information&amp;plugin={$plugin_slug}&amp;section=&amp;TB_iframe=true&amp;width=640&amp;height=550" );
1173
 
1174
  if ( 'plugin_updated' == $message_key || 'plugin_bulk_updated' == $message_key ) {
1175
 
@@ -1201,7 +1205,7 @@ class SimplePluginLogger extends SimpleLogger {
1201
  esc_html_x( 'View plugin info', 'plugin logger: plugin info thickbox title view all info', 'simple-history' )
1202
  );
1203
 
1204
- }// End if().
1205
  } // End if().
1206
 
1207
  return $output;
3
  defined( 'ABSPATH' ) || die();
4
 
5
  /**
6
+ * Logs plugin installs, updates, and deletions.
7
  */
8
  class SimplePluginLogger extends SimpleLogger {
9
 
10
  /**
11
+ * The logger slug. Defaulting to the class name is nice and logical I think.
12
  *
13
  * @var string $slug
14
  */
28
  *
29
  * @return array
30
  */
31
+ public function getInfo() {
32
 
33
  $arr_info = array(
34
+ 'name' => 'Plugin Logger',
35
  'description' => 'Logs plugin installs, uninstalls and updates',
36
+ 'capability' => 'activate_plugins',
37
+ 'messages' => array(
38
 
39
+ 'plugin_activated' => _x(
40
  'Activated plugin "{plugin_name}"',
41
  'Plugin was non-silently activated by a user',
42
  'simple-history'
43
  ),
44
 
45
+ 'plugin_deactivated' => _x(
46
  'Deactivated plugin "{plugin_name}"',
47
  'Plugin was non-silently deactivated by a user',
48
  'simple-history'
49
  ),
50
 
51
+ 'plugin_installed' => _x(
52
  'Installed plugin "{plugin_name}"',
53
  'Plugin was installed',
54
  'simple-history'
55
  ),
56
 
57
+ 'plugin_installed_failed' => _x(
58
  'Failed to install plugin "{plugin_name}"',
59
  'Plugin failed to install',
60
  'simple-history'
61
  ),
62
 
63
+ 'plugin_updated' => _x(
64
  'Updated plugin "{plugin_name}" to version {plugin_version} from {plugin_prev_version}',
65
  'Plugin was updated',
66
  'simple-history'
67
  ),
68
 
69
+ 'plugin_update_failed' => _x(
70
  'Failed to update plugin "{plugin_name}"',
71
  'Plugin update failed',
72
  'simple-history'
73
  ),
74
 
75
+ 'plugin_deleted' => _x(
76
  'Deleted plugin "{plugin_name}"',
77
  'Plugin files was deleted',
78
  'simple-history'
79
  ),
80
 
81
  // Bulk versions.
82
+ 'plugin_bulk_updated' => _x(
83
  'Updated plugin "{plugin_name}" to {plugin_version} from {plugin_prev_version}',
84
  'Plugin was updated in bulk',
85
  'simple-history'
93
  ),
94
 
95
  ), // Messages.
96
+ 'labels' => array(
97
  'search' => array(
98
+ 'label' => _x( 'Plugins', 'Plugin logger: search', 'simple-history' ),
99
  'label_all' => _x( 'All plugin activity', 'Plugin logger: search', 'simple-history' ),
100
+ 'options' => array(
101
  _x( 'Activated plugins', 'Plugin logger: search', 'simple-history' ) => array(
102
+ 'plugin_activated',
103
  ),
104
  _x( 'Deactivated plugins', 'Plugin logger: search', 'simple-history' ) => array(
105
  'plugin_deactivated',
106
  'plugin_disabled_because_error',
107
  ),
108
  _x( 'Installed plugins', 'Plugin logger: search', 'simple-history' ) => array(
109
+ 'plugin_installed',
110
  ),
111
  _x( 'Failed plugin installs', 'Plugin logger: search', 'simple-history' ) => array(
112
+ 'plugin_installed_failed',
113
  ),
114
  _x( 'Updated plugins', 'Plugin logger: search', 'simple-history' ) => array(
115
  'plugin_updated',
116
  'plugin_bulk_updated',
117
  ),
118
  _x( 'Failed plugin updates', 'Plugin logger: search', 'simple-history' ) => array(
119
+ 'plugin_update_failed',
120
  ),
121
  _x( 'Deleted plugins', 'Plugin logger: search', 'simple-history' ) => array(
122
+ 'plugin_deleted',
123
  ),
124
  ),
125
  ), // search array.
154
  // this hook does not fire.
155
  add_action( 'deactivated_plugin', array( $this, 'on_deactivated_plugin' ), 10, 2 );
156
 
157
+ // Fires after the upgrades has done it's thing.
158
+ // Check hook extra for upgrader initiator.
159
  add_action( 'upgrader_process_complete', array( $this, 'on_upgrader_process_complete' ), 10, 2 );
160
 
161
+ // Detect files removed.
162
  add_action( 'setted_transient', array( $this, 'on_setted_transient_for_remove_files' ), 10, 2 );
163
 
164
  add_action( 'admin_action_delete-selected', array( $this, 'on_action_delete_selected' ), 10, 1 );
165
 
166
+ // Ajax function to get info from GitHub repo. Used by "View plugin info"-link for plugin installs.
167
  add_action( 'wp_ajax_SimplePluginLogger_GetGitHubPluginInfo', array( $this, 'ajax_GetGitHubPluginInfo' ) );
168
 
169
  // If the Github Update plugin is not installed we need to get extra fields used by it.
170
  // So need to hook filter "extra_plugin_headers" ourself.
171
+ add_filter(
172
+ 'extra_plugin_headers', function( $arr_headers ) {
173
+ $arr_headers[] = 'GitHub Plugin URI';
174
+ return $arr_headers;
175
+ }
176
+ );
177
 
178
  // There is no way to use a filter and detect a plugin that is disabled because it can't be found or similar error.
179
  // So we hook into gettext and look for the usage of the error that is returned when this happens.
231
  * we hook into gettext and look for the usage of the error that is returned when this happens.
232
  *
233
  * A plugin gets deactivated when plugins.php is visited function validate_active_plugins()
234
+ * return new WP_Error('plugin_not_found', __('Plugin file does not exist.'));
235
  * and if invalid plugin is found then this is outputed
236
  * printf(
237
  * /* translators: 1: plugin file 2: error message
248
  global $pagenow;
249
 
250
  // We only act on page plugins.php.
251
+ if ( ! isset( $pagenow ) || 'plugins.php' !== $pagenow ) {
252
  return $translation;
253
  }
254
 
256
  // (Literally these, no translation)
257
  $untranslated_texts = array(
258
  // This string is called later than the above
259
+ 'The plugin %1$s has been <strong>deactivated</strong> due to an error: %2$s',
260
  );
261
 
262
  if ( ! in_array( $text, $untranslated_texts ) ) {
269
  // stuff the first time it's called (directly after the gettet for the plugin disabled-error..).
270
  $logger_instance = $this;
271
 
272
+ add_filter(
273
+ 'esc_html', function( $safe_text, $text ) use ( $logger_instance ) {
274
+ static $is_called = false;
275
 
276
+ if ( false === $is_called ) {
277
+ $is_called = true;
278
 
279
+ $deactivation_reason = array_shift( $logger_instance->latest_plugin_deactivation_because_of_error_reason );
280
 
281
+ // We don't know what plugin that was that got this error and currently there does not seem to be a way to determine that.
282
+ // So that's why we use such generic log messages.
283
+ $logger_instance->warningMessage(
284
+ 'plugin_disabled_because_error',
285
+ array(
286
+ '_initiator' => SimpleLoggerLogInitiators::WORDPRESS,
287
+ 'plugin_slug' => $text,
288
+ 'deactivation_reason' => $deactivation_reason,
289
+ )
290
+ );
291
+ }
292
 
293
+ return $safe_text;
294
+ }, 10, 2
295
+ );
296
 
297
  return $translation;
298
 
319
  }
320
 
321
  $repo_username = $repo_parts[3];
322
+ $repo_repo = $repo_parts[4];
323
 
324
  // https://developer.github.com/v3/repos/contents/
325
  // https://api.github.com/repos/<username>/<repo>/readme
326
  $api_url = sprintf( 'https://api.github.com/repos/%1$s/%2$s/readme', urlencode( $repo_username ), urlencode( $repo_repo ) );
327
 
328
  // Get file. Use accept-header to get file as HTML instead of JSON
329
+ $response = wp_remote_get(
330
+ $api_url, array(
331
+ 'headers' => array(
332
+ 'accept' => 'application/vnd.github.VERSION.html',
333
+ ),
334
+ )
335
+ );
336
 
337
  $response_body = wp_remote_retrieve_body( $response );
338
 
464
  ) {
465
 
466
  /*
467
+ [checked] => Array
468
+ (
469
+ [0] => the-events-calendar/the-events-calendar.php
470
+ )
471
+ */
472
 
473
+ $plugins_deleted = $_POST['checked'];
474
  $plugins_before_update = json_decode( get_option( $this->slug . '_plugin_info_before_update', false ), true );
475
 
476
  foreach ( $plugins_deleted as $plugin ) {
477
 
478
  $context = array(
479
+ 'plugin' => $plugin, // plugin-name-folder/plugin-main-file.php
480
  );
481
 
482
  if ( is_array( $plugins_before_update ) && isset( $plugins_before_update[ $plugin ] ) ) {
483
+ $context['plugin_name'] = $plugins_before_update[ $plugin ]['Name'];
484
+ $context['plugin_title'] = $plugins_before_update[ $plugin ]['Title'];
485
  $context['plugin_description'] = $plugins_before_update[ $plugin ]['Description'];
486
+ $context['plugin_author'] = $plugins_before_update[ $plugin ]['Author'];
487
+ $context['plugin_version'] = $plugins_before_update[ $plugin ]['Version'];
488
+ $context['plugin_url'] = $plugins_before_update[ $plugin ]['PluginURI'];
489
  }
490
 
491
  $this->infoMessage(
571
  If an update fails then $plugin_upgrader_instance->skin->result->errors contains something like:
572
  Array
573
  (
574
+ [remove_old_failed] => Array
575
+ (
576
+ [0] => Could not remove the old plugin.
577
+ )
578
 
579
  )
580
  */
587
  $arr_data:
588
  Array
589
  (
590
+ [action] => update
591
+ [type] => core
592
  )
593
 
594
 
597
  $arr_data:
598
  Array
599
  (
600
+ [type] => plugin
601
+ [action] => install
602
  )
603
 
604
 
607
  $arr_data:
608
  Array
609
  (
610
+ [type] => plugin
611
+ [action] => install
612
  )
613
 
614
  ## Bulk actions
632
  if ( isset( $arr_data['action'] ) && 'install' == $arr_data['action'] && ! $plugin_upgrader_instance->bulk ) {
633
 
634
  $upgrader_skin_options = isset( $plugin_upgrader_instance->skin->options ) && is_array( $plugin_upgrader_instance->skin->options ) ? $plugin_upgrader_instance->skin->options : array();
635
+ $upgrader_skin_result = isset( $plugin_upgrader_instance->skin->result ) && is_array( $plugin_upgrader_instance->skin->result ) ? $plugin_upgrader_instance->skin->result : array();
636
+ $upgrader_skin_api = isset( $plugin_upgrader_instance->skin->api ) ? $plugin_upgrader_instance->skin->api : (object) array();
637
 
638
  $plugin_slug = isset( $upgrader_skin_result['destination_name'] ) ? $upgrader_skin_result['destination_name'] : '';
639
 
640
  // Upgrader contains current info
641
  $context = array(
642
+ 'plugin_slug' => $plugin_slug,
643
+ 'plugin_name' => isset( $upgrader_skin_api->name ) ? $upgrader_skin_api->name : '',
644
+ 'plugin_version' => isset( $upgrader_skin_api->version ) ? $upgrader_skin_api->version : '',
645
+ 'plugin_author' => isset( $upgrader_skin_api->author ) ? $upgrader_skin_api->author : '',
646
  'plugin_last_updated' => isset( $upgrader_skin_api->last_updated ) ? $upgrader_skin_api->last_updated : '',
647
+ 'plugin_requires' => isset( $upgrader_skin_api->requires ) ? $upgrader_skin_api->requires : '',
648
+ 'plugin_tested' => isset( $upgrader_skin_api->tested ) ? $upgrader_skin_api->tested : '',
649
+ 'plugin_rating' => isset( $upgrader_skin_api->rating ) ? $upgrader_skin_api->rating : '',
650
+ 'plugin_num_ratings' => isset( $upgrader_skin_api->num_ratings ) ? $upgrader_skin_api->num_ratings : '',
651
+ 'plugin_downloaded' => isset( $upgrader_skin_api->downloaded ) ? $upgrader_skin_api->downloaded : '',
652
+ 'plugin_added' => isset( $upgrader_skin_api->added ) ? $upgrader_skin_api->added : '',
653
  'plugin_source_files' => $this->simpleHistory->json_encode( $plugin_upgrader_instance->result['source_files'] ),
654
 
655
  // To debug comment out these:
680
  /*
681
  _debug_files
682
  {
683
+ "pluginzip": {
684
+ "name": "WPThumb-master.zip",
685
+ "type": "application\/zip",
686
+ "tmp_name": "\/Applications\/MAMP\/tmp\/php\/phpnThImc",
687
+ "error": 0,
688
+ "size": 2394625
689
+ }
690
  }
691
  */
692
 
693
  if ( isset( $_FILES['pluginzip']['name'] ) ) {
694
+ $plugin_upload_name = $_FILES['pluginzip']['name'];
695
  $context['plugin_upload_name'] = $plugin_upload_name;
696
  }
697
  }
701
  // Add errors
702
  // Errors is in original wp admin language
703
  $context['error_messages'] = $this->simpleHistory->json_encode( $plugin_upgrader_instance->skin->result->errors );
704
+ $context['error_data'] = $this->simpleHistory->json_encode( $plugin_upgrader_instance->skin->result->error_data );
705
 
706
  $this->infoMessage(
707
  'plugin_installed_failed',
726
  $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_info, true, false );
727
  }
728
 
729
+ $context['plugin_name'] = isset( $plugin_data['Name'] ) ? $plugin_data['Name'] : '';
730
  $context['plugin_description'] = isset( $plugin_data['Description'] ) ? $plugin_data['Description'] : '';
731
+ $context['plugin_url'] = isset( $plugin_data['PluginURI'] ) ? $plugin_data['PluginURI'] : '';
732
+ $context['plugin_version'] = isset( $plugin_data['Version'] ) ? $plugin_data['Version'] : '';
733
+ $context['plugin_author'] = isset( $plugin_data['AuthorName'] ) ? $plugin_data['AuthorName'] : '';
734
 
735
  // Comment out these to debug plugin installs
736
  // $context["debug_plugin_data"] = $this->simpleHistory->json_encode( $plugin_data );
763
  $plugin_slug = dirname( $arr_data['plugin'] );
764
 
765
  $context = array(
766
+ 'plugin_slug' => $plugin_slug,
767
+ 'request' => $this->simpleHistory->json_encode( $_REQUEST ),
768
+ 'plugin_name' => $plugin_data['Name'],
769
+ 'plugin_title' => $plugin_data['Title'],
770
+ 'plugin_description' => $plugin_data['Description'],
771
+ 'plugin_author' => $plugin_data['Author'],
772
+ 'plugin_version' => $plugin_data['Version'],
773
+ 'plugin_url' => $plugin_data['PluginURI'],
774
  'plugin_source_files' => $this->simpleHistory->json_encode( $plugin_upgrader_instance->result['source_files'] ),
775
  );
776
 
819
  // Add errors
820
  // Errors is in original wp admin language
821
  $context['error_messages'] = json_encode( $plugin_upgrader_instance->skin->result->errors );
822
+ $context['error_data'] = json_encode( $plugin_upgrader_instance->skin->result->error_data );
823
 
824
  $this->infoMessage(
825
  'plugin_update_failed',
868
  $plugin_slug = dirname( $plugin_name );
869
 
870
  $context = array(
871
+ 'plugin_slug' => $plugin_slug,
872
+ 'plugin_name' => $plugin_data['Name'],
873
+ 'plugin_title' => $plugin_data['Title'],
874
  'plugin_description' => $plugin_data['Description'],
875
+ 'plugin_author' => $plugin_data['Author'],
876
+ 'plugin_version' => $plugin_data['Version'],
877
+ 'plugin_url' => $plugin_data['PluginURI'],
878
  );
879
 
880
  // get url and package
960
  $plugin_slug = dirname( $plugin_name );
961
 
962
  $context = array(
963
+ 'plugin_name' => $plugin_data['Name'],
964
+ 'plugin_slug' => $plugin_slug,
965
+ 'plugin_title' => $plugin_data['Title'],
966
  'plugin_description' => $plugin_data['Description'],
967
+ 'plugin_author' => $plugin_data['Author'],
968
+ 'plugin_version' => $plugin_data['Version'],
969
+ 'plugin_url' => $plugin_data['PluginURI'],
970
  );
971
 
972
  if ( ! empty( $plugin_data['GitHub Plugin URI'] ) ) {
987
  $plugin_slug = dirname( $plugin_name );
988
 
989
  $context = array(
990
+ 'plugin_name' => $plugin_data['Name'],
991
+ 'plugin_slug' => $plugin_slug,
992
+ 'plugin_title' => $plugin_data['Title'],
993
  'plugin_description' => $plugin_data['Description'],
994
+ 'plugin_author' => $plugin_data['Author'],
995
+ 'plugin_version' => $plugin_data['Version'],
996
+ 'plugin_url' => $plugin_data['PluginURI'],
997
  );
998
 
999
  if ( ! empty( $plugin_data['GitHub Plugin URI'] ) ) {
1010
  */
1011
  function getLogRowDetailsOutput( $row ) {
1012
 
1013
+ $context = $row->context;
1014
  $message_key = $context['_message_key'];
1015
+ $output = '';
1016
 
1017
  // When a plugin is installed we show a bit more information
1018
  // We do it only on install because we don't want to clutter to log,
1024
 
1025
  // Description includes a link to author, remove that, i.e. all text after and including <cite>
1026
  $plugin_description = $context['plugin_description'];
1027
+ $cite_pos = strpos( $plugin_description, '<cite>' );
1028
  if ( $cite_pos ) {
1029
  $plugin_description = substr( $plugin_description, 0, $cite_pos );
1030
  }
1031
 
1032
  // Keys to show
1033
  $arr_plugin_keys = array(
1034
+ 'plugin_description' => _x( 'Description', 'plugin logger - detailed output', 'simple-history' ),
1035
+ 'plugin_install_source' => _x( 'Source', 'plugin logger - detailed output install source', 'simple-history' ),
1036
  'plugin_install_source_file' => _x( 'Source file name', 'plugin logger - detailed output install source', 'simple-history' ),
1037
+ 'plugin_version' => _x( 'Version', 'plugin logger - detailed output version', 'simple-history' ),
1038
+ 'plugin_author' => _x( 'Author', 'plugin logger - detailed output author', 'simple-history' ),
1039
+ 'plugin_url' => _x( 'URL', 'plugin logger - detailed output url', 'simple-history' ),
1040
  // "plugin_downloaded" => _x("Downloads", "plugin logger - detailed output downloaded", "simple-history"),
1041
  // "plugin_requires" => _x("Requires", "plugin logger - detailed output author", "simple-history"),
1042
  // "plugin_tested" => _x("Compatible up to", "plugin logger - detailed output compatible", "simple-history"),
1073
  break;
1074
 
1075
  case 'plugin_install_source':
 
1076
  if ( ! isset( $context[ $key ] ) ) {
1077
  continue;
1078
  }
1091
  break;
1092
 
1093
  case 'plugin_install_source_file':
 
1094
  if ( ! isset( $context['plugin_upload_name'] ) || ! isset( $context['plugin_install_source'] ) ) {
1095
  continue;
1096
  }
1097
 
1098
  if ( 'upload' == $context['plugin_install_source'] ) {
1099
  $plugin_upload_name = $context['plugin_upload_name'];
1100
+ $desc_output = esc_html( $plugin_upload_name );
1101
  }
1102
 
1103
  break;
1173
  if ( $plugin_slug && empty( $context['plugin_github_url'] ) ) {
1174
 
1175
  $link_title = esc_html_x( 'View plugin info', 'plugin logger: plugin info thickbox title', 'simple-history' );
1176
+ $url = admin_url( "plugin-install.php?tab=plugin-information&amp;plugin={$plugin_slug}&amp;section=&amp;TB_iframe=true&amp;width=640&amp;height=550" );
1177
 
1178
  if ( 'plugin_updated' == $message_key || 'plugin_bulk_updated' == $message_key ) {
1179
 
1205
  esc_html_x( 'View plugin info', 'plugin logger: plugin info thickbox title view all info', 'simple-history' )
1206
  );
1207
 
1208
+ } // End if().
1209
  } // End if().
1210
 
1211
  return $output;
loggers/SimplePostLogger.php CHANGED
@@ -2,7 +2,6 @@
2
 
3
  defined( 'ABSPATH' ) or die();
4
 
5
-
6
  /**
7
  * Logs changes to posts and pages, including custom post types
8
  */
@@ -175,9 +174,13 @@ class SimplePostLogger extends SimpleLogger {
175
 
176
  $prev_post_data = get_post( $post_ID );
177
 
178
- $this->old_post_data[ $post_ID ] = array(
179
- 'post_data' => $prev_post_data,
180
- 'post_meta' => get_post_custom( $post_ID ),
 
 
 
 
181
  );
182
 
183
  }
@@ -255,6 +258,10 @@ class SimplePostLogger extends SimpleLogger {
255
 
256
  $post = get_post( $post_id );
257
 
 
 
 
 
258
  $this->infoMessage(
259
  'post_restored',
260
  array(
@@ -315,13 +322,52 @@ class SimplePostLogger extends SimpleLogger {
315
 
316
  }
317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
  /**
320
- * Fired when a post has changed status
321
- * Only run in certain cases,
322
- * because when always enabled it catches a lots of edits made by plugins during cron jobs etc,
323
- * which by definition is not wrong, but perhaps not wanted/annoying
324
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  function on_transition_post_status( $new_status, $old_status, $post ) {
326
 
327
  $ok_to_log = true;
@@ -344,25 +390,23 @@ class SimplePostLogger extends SimpleLogger {
344
  $ok_to_log = false;
345
  }
346
 
347
- // Don't log some post types.
348
- $skip_posttypes = array(
349
- // Don't log nav_menu_updates.
350
- 'nav_menu_item',
351
- // Don't log jetpack migration-things.
352
- // https://wordpress.org/support/topic/updated-jetpack_migration-sidebars_widgets/.
353
- 'jetpack_migration',
354
- );
355
 
356
  /**
357
- * Filter to log what post types not to log
358
  *
359
- * @since 2.18
 
 
 
 
 
 
 
360
  */
361
- $skip_posttypes = apply_filters( 'simple_history/post_logger/skip_posttypes', $skip_posttypes );
362
-
363
- if ( in_array( get_post_type( $post ), $skip_posttypes, true ) ) {
364
- $ok_to_log = false;
365
- }
366
 
367
  if ( ! $ok_to_log ) {
368
  return;
@@ -421,7 +465,15 @@ class SimplePostLogger extends SimpleLogger {
421
 
422
  $context['_occasionsID'] = __CLASS__ . '/' . __FUNCTION__ . "/post_updated/{$post->ID}";
423
 
424
- $this->infoMessage( 'post_updated', $context );
 
 
 
 
 
 
 
 
425
 
426
  }// End if().
427
 
@@ -654,7 +706,7 @@ class SimplePostLogger extends SimpleLogger {
654
  }
655
 
656
  /**
657
- * Modify plain output to include link to post
658
  *
659
  * @param array $row Row data.
660
  */
@@ -960,6 +1012,15 @@ class SimplePostLogger extends SimpleLogger {
960
  // post_new_thumb, int of new thumb, empty if no new thumb.
961
  $diff_table_output .= $this->getLogRowDetailsOutputForPostThumb( $context );
962
 
 
 
 
 
 
 
 
 
 
963
  if ( $has_diff_values || $diff_table_output ) {
964
 
965
  $diff_table_output = '<table class="SimpleHistoryLogitem__keyValueTable">' . $diff_table_output . '</table>';
@@ -1060,6 +1121,8 @@ class SimplePostLogger extends SimpleLogger {
1060
  // Check if images still exists and if so get their thumbnails.
1061
  $prev_thumb_id = empty( $context['post_prev_thumb_id'] ) ? null : $context['post_prev_thumb_id'];
1062
  $new_thumb_id = empty( $context['post_new_thumb_id'] ) ? null : $context['post_new_thumb_id'];
 
 
1063
 
1064
  $prev_attached_file = get_attached_file( $prev_thumb_id );
1065
  $prev_thumb_src = wp_get_attachment_image_src( $prev_thumb_id, 'small' );
@@ -1077,13 +1140,13 @@ class SimplePostLogger extends SimpleLogger {
1077
  </div>
1078
  ',
1079
  $prev_thumb_src[0],
1080
- esc_html( $context['post_prev_thumb_title'] )
1081
  );
1082
  } else {
1083
  // Fallback if image does not exist.
1084
  $prev_thumb_html = sprintf(
1085
  '<div>%1$s</div>',
1086
- esc_html( $context['post_prev_thumb_title'] )
1087
  );
1088
  }
1089
 
@@ -1097,13 +1160,13 @@ class SimplePostLogger extends SimpleLogger {
1097
  </div>
1098
  ',
1099
  $new_thumb_src[0],
1100
- esc_html( $context['post_new_thumb_title'] )
1101
  );
1102
  } else {
1103
  // Fallback if image does not exist.
1104
  $prev_thumb_html = sprintf(
1105
  '<div>%1$s</div>',
1106
- esc_html( $context['post_new_thumb_title'] )
1107
  );
1108
  }
1109
 
@@ -1131,7 +1194,7 @@ class SimplePostLogger extends SimpleLogger {
1131
  </td>
1132
  </tr>',
1133
  esc_html( __( 'Featured image', 'simple-history' ) ), // 1
1134
- $prev_thumb_html,// 2
1135
  $new_thumb_html // 3
1136
  );
1137
  } // End if().
2
 
3
  defined( 'ABSPATH' ) or die();
4
 
 
5
  /**
6
  * Logs changes to posts and pages, including custom post types
7
  */
174
 
175
  $prev_post_data = get_post( $post_ID );
176
 
177
+ if (is_wp_error($prev_post_data)) {
178
+ return;
179
+ }
180
+
181
+ $this->old_post_data[$post_ID] = array(
182
+ "post_data" => $prev_post_data,
183
+ "post_meta" => get_post_custom( $post_ID )
184
  );
185
 
186
  }
258
 
259
  $post = get_post( $post_id );
260
 
261
+ if ( ! $this->ok_to_log_post_posttype( $post ) ) {
262
+ return;
263
+ }
264
+
265
  $this->infoMessage(
266
  'post_restored',
267
  array(
322
 
323
  }
324
 
325
+ /**
326
+ * Get an array of post types that should not be logged by this logger.
327
+ */
328
+ public function get_skip_posttypes() {
329
+ $skip_posttypes = array(
330
+ // Don't log nav_menu_updates.
331
+ 'nav_menu_item',
332
+ // Don't log jetpack migration-things.
333
+ // https://wordpress.org/support/topic/updated-jetpack_migration-sidebars_widgets/.
334
+ 'jetpack_migration',
335
+ 'jp_sitemap',
336
+ 'jp_img_sitemap',
337
+ 'jp_sitemap_master',
338
+ );
339
+
340
+ /**
341
+ * Filter to log what post types not to log
342
+ *
343
+ * @since 2.18
344
+ */
345
+ $skip_posttypes = apply_filters( 'simple_history/post_logger/skip_posttypes', $skip_posttypes );
346
+
347
+ return $skip_posttypes;
348
+ }
349
 
350
  /**
351
+ * Check if post type is ok to log by logger
352
+ * @return bool
 
 
353
  */
354
+ public function ok_to_log_post_posttype( $post ) {
355
+ $ok_to_log = true;
356
+ $skip_posttypes = $this->get_skip_posttypes();
357
+
358
+ if ( in_array( get_post_type( $post ), $skip_posttypes, true ) ) {
359
+ $ok_to_log = false;
360
+ }
361
+
362
+ return $ok_to_log;
363
+ }
364
+
365
+ /**
366
+ * Fired when a post has changed status
367
+ * Only run in certain cases,
368
+ * because when always enabled it catches a lots of edits made by plugins during cron jobs etc,
369
+ * which by definition is not wrong, but perhaps not wanted/annoying
370
+ */
371
  function on_transition_post_status( $new_status, $old_status, $post ) {
372
 
373
  $ok_to_log = true;
390
  $ok_to_log = false;
391
  }
392
 
393
+ if ( ! $this->ok_to_log_post_posttype( $post ) ) {
394
+ $ok_to_log = false;
395
+ }
 
 
 
 
 
396
 
397
  /**
398
+ * Filter to control logging.
399
  *
400
+ * @param bool $ok_to_log
401
+ * @param $new_status
402
+ * @param $old_status
403
+ * @param $post
404
+ *
405
+ * @return bool True to log, false to not log.
406
+ *
407
+ * @since 2.21
408
  */
409
+ $ok_to_log = apply_filters( 'simple_history/post_logger/post_updated/ok_to_log', $ok_to_log, $new_status, $old_status, $post );
 
 
 
 
410
 
411
  if ( ! $ok_to_log ) {
412
  return;
465
 
466
  $context['_occasionsID'] = __CLASS__ . '/' . __FUNCTION__ . "/post_updated/{$post->ID}";
467
 
468
+ /**
469
+ * Modify the context saved.
470
+ *
471
+ * @param array $context
472
+ * @param WP_Post $post
473
+ */
474
+ $context = apply_filters( 'simple_history/post_logger/post_updated/context', $context, $post );
475
+
476
+ $this->infoMessage( "post_updated", $context );
477
 
478
  }// End if().
479
 
706
  }
707
 
708
  /**
709
+ * Modify plain output to include link to post.
710
  *
711
  * @param array $row Row data.
712
  */
1012
  // post_new_thumb, int of new thumb, empty if no new thumb.
1013
  $diff_table_output .= $this->getLogRowDetailsOutputForPostThumb( $context );
1014
 
1015
+ /**
1016
+ * Modify the formatted diff output of a saved/modified post
1017
+ *
1018
+ * @param string $diff_table_output
1019
+ * @param array $context
1020
+ * @return string
1021
+ */
1022
+ $diff_table_output = apply_filters( 'simple_history/post_logger/post_updated/diff_table_output', $diff_table_output, $context );
1023
+
1024
  if ( $has_diff_values || $diff_table_output ) {
1025
 
1026
  $diff_table_output = '<table class="SimpleHistoryLogitem__keyValueTable">' . $diff_table_output . '</table>';
1121
  // Check if images still exists and if so get their thumbnails.
1122
  $prev_thumb_id = empty( $context['post_prev_thumb_id'] ) ? null : $context['post_prev_thumb_id'];
1123
  $new_thumb_id = empty( $context['post_new_thumb_id'] ) ? null : $context['post_new_thumb_id'];
1124
+ $post_new_thumb_title = empty( $context['post_new_thumb_title'] ) ? null : $context['post_new_thumb_title'];
1125
+ $post_prev_thumb_title = empty( $context['post_prev_thumb_title'] ) ? null : $context['post_prev_thumb_title'];
1126
 
1127
  $prev_attached_file = get_attached_file( $prev_thumb_id );
1128
  $prev_thumb_src = wp_get_attachment_image_src( $prev_thumb_id, 'small' );
1140
  </div>
1141
  ',
1142
  $prev_thumb_src[0],
1143
+ esc_html( $post_prev_thumb_title )
1144
  );
1145
  } else {
1146
  // Fallback if image does not exist.
1147
  $prev_thumb_html = sprintf(
1148
  '<div>%1$s</div>',
1149
+ esc_html( $post_prev_thumb_title )
1150
  );
1151
  }
1152
 
1160
  </div>
1161
  ',
1162
  $new_thumb_src[0],
1163
+ esc_html( $post_new_thumb_title )
1164
  );
1165
  } else {
1166
  // Fallback if image does not exist.
1167
  $prev_thumb_html = sprintf(
1168
  '<div>%1$s</div>',
1169
+ esc_html( $post_new_thumb_title )
1170
  );
1171
  }
1172
 
1194
  </td>
1195
  </tr>',
1196
  esc_html( __( 'Featured image', 'simple-history' ) ), // 1
1197
+ $prev_thumb_html, // 2
1198
  $new_thumb_html // 3
1199
  );
1200
  } // End if().
loggers/SimpleThemeLogger.php CHANGED
@@ -963,22 +963,22 @@ class SimpleThemeLogger extends SimpleLogger {
963
 
964
  $context = array();
965
 
966
- // Add widget info
967
  $context['widget_id_base'] = $widget_id_base;
968
  $widget = $this->getWidgetByIdBase( $widget_id_base );
969
  if ( $widget ) {
970
  $context['widget_name_translated'] = $widget->name;
971
  }
972
 
973
- // Add sidebar info
974
- $sidebar_id = $_POST['sidebar'];
975
  $context['sidebar_id'] = $sidebar_id;
976
  $sidebar = $this->getSidebarById( $sidebar_id );
977
  if ( $sidebar ) {
978
  $context['sidebar_name_translated'] = $sidebar['name'];
979
  }
980
 
981
- // Calculate changes
982
  $context['old_instance'] = $this->simpleHistory->json_encode( $old_instance );
983
  $context['new_instance'] = $this->simpleHistory->json_encode( $new_instance );
984
 
@@ -1099,18 +1099,22 @@ class SimpleThemeLogger extends SimpleLogger {
1099
  /**
1100
  * Get a sidebar by id
1101
  *
1102
- * @param string $sidebar_id
1103
- * @return sidebar info or false on failure
1104
  */
1105
  function getSidebarById( $sidebar_id ) {
1106
 
 
 
 
 
1107
  $sidebars = isset( $GLOBALS['wp_registered_sidebars'] ) ? $GLOBALS['wp_registered_sidebars'] : false;
1108
 
1109
  if ( ! $sidebars ) {
1110
  return false;
1111
  }
1112
 
1113
- // Add sidebar info
1114
  if ( isset( $sidebars[ $sidebar_id ] ) ) {
1115
 
1116
  return $sidebars[ $sidebar_id ];
963
 
964
  $context = array();
965
 
966
+ // Add widget info.
967
  $context['widget_id_base'] = $widget_id_base;
968
  $widget = $this->getWidgetByIdBase( $widget_id_base );
969
  if ( $widget ) {
970
  $context['widget_name_translated'] = $widget->name;
971
  }
972
 
973
+ // Add sidebar info.
974
+ $sidebar_id = isset( $_POST['sidebar'] ) ? $_POST['sidebar'] : null;
975
  $context['sidebar_id'] = $sidebar_id;
976
  $sidebar = $this->getSidebarById( $sidebar_id );
977
  if ( $sidebar ) {
978
  $context['sidebar_name_translated'] = $sidebar['name'];
979
  }
980
 
981
+ // Calculate changes.
982
  $context['old_instance'] = $this->simpleHistory->json_encode( $old_instance );
983
  $context['new_instance'] = $this->simpleHistory->json_encode( $new_instance );
984
 
1099
  /**
1100
  * Get a sidebar by id
1101
  *
1102
+ * @param string $sidebar_id ID of sidebar.
1103
+ * @return sidebar info or false on failure.
1104
  */
1105
  function getSidebarById( $sidebar_id ) {
1106
 
1107
+ if ( empty( $sidebar_id ) ) {
1108
+ return false;
1109
+ }
1110
+
1111
  $sidebars = isset( $GLOBALS['wp_registered_sidebars'] ) ? $GLOBALS['wp_registered_sidebars'] : false;
1112
 
1113
  if ( ! $sidebars ) {
1114
  return false;
1115
  }
1116
 
1117
+ // Add sidebar info.
1118
  if ( isset( $sidebars[ $sidebar_id ] ) ) {
1119
 
1120
  return $sidebars[ $sidebar_id ];
loggers/SimpleUserLogger.php CHANGED
@@ -5,7 +5,6 @@
5
  */
6
  class SimpleUserLogger extends SimpleLogger {
7
 
8
-
9
  public $slug = __CLASS__;
10
 
11
  /**
@@ -466,8 +465,6 @@ class SimpleUserLogger extends SimpleLogger {
466
 
467
  $use_you = apply_filters( 'simple_history/user_logger/plain_text_output_use_you', true );
468
 
469
- // error_log( serialize($current_user_id) ); // int 1
470
- // error_log( serialize($context["_user_id"]) ); // string 1
471
  // User still exist, so link to their profile
472
  if ( (int) $current_user_id === (int) $context['_user_id'] && $use_you ) {
473
  // User that is viewing the log is the same as the edited user
5
  */
6
  class SimpleUserLogger extends SimpleLogger {
7
 
 
8
  public $slug = __CLASS__;
9
 
10
  /**
465
 
466
  $use_you = apply_filters( 'simple_history/user_logger/plain_text_output_use_you', true );
467
 
 
 
468
  // User still exist, so link to their profile
469
  if ( (int) $current_user_id === (int) $context['_user_id'] && $use_you ) {
470
  // User that is viewing the log is the same as the edited user
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: history, log, changes, changelog, audit, trail, pages, attachments, users,
5
  Requires at least: 4.5.1
6
  Tested up to: 4.9
7
  Requires PHP: 5.3
8
- Stable tag: 2.20
9
 
10
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
11
 
@@ -159,11 +159,28 @@ initiated by a specific user.
159
  7. A chart with some quick statistics is available, so you can see the number of events that has been logged each day.
160
  A simple way to see any uncommon activity, for example an increased number of logins or similar.
161
 
162
- ==
163
- ==
164
 
165
  ## Changelog
166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  = 2.20 (November 2017) =
168
 
169
  - Add logging of post thumbnails.
@@ -172,7 +189,7 @@ A simple way to see any uncommon activity, for example an increased number of lo
172
  - Update Select2 to latest version. Fixes https://wordpress.org/support/topic/select2-js-is-outdated/.
173
  - Show a message if user is running to old WordPress version, and don't continue running the code of this plugin.
174
  Should fix stuff like https://wordpress.org/support/topic/simple-history-i-cannot-login/.
175
- - Fix an error with PHP 7.1.
176
 
177
  = 2.19 (November 2017) =
178
 
5
  Requires at least: 4.5.1
6
  Tested up to: 4.9
7
  Requires PHP: 5.3
8
+ Stable tag: 2.21
9
 
10
  View changes made by users within WordPress. See who created a page, uploaded an attachment or approved an comment, and more.
11
 
159
  7. A chart with some quick statistics is available, so you can see the number of events that has been logged each day.
160
  A simple way to see any uncommon activity, for example an increased number of logins or similar.
161
 
162
+ == Changelog ==
 
163
 
164
  ## Changelog
165
 
166
+ = 2.21 (May 2018) =
167
+
168
+ - Added support for Advanced Custom Fields (ACF): when a ACF Field or ACF Field Group is created or modified or deleted you will now get more details in the activity feed.
169
+ - Changes to taxonomies/categories/tags now include a link to the modified term and to the category that the term belongs to.
170
+ - The post types in the `skip_posttypes` filter are now also applied to trashed and untrashed posts (not only post edits, as before).
171
+ - Don't log Jetpack sitemap updates. (Don't log updates to posttypes `jp_sitemap`, `jp_sitemap_master` and `jp_img_sitemap`, i.e. the post types used by Jetpack's Sitemap function.) Should fix https://wordpress.org/support/topic/jetpack-sitemap-logging/.
172
+ - Don't log the taxonomies `post_translations` or `term_translations`, that are used by Polylang to store translation mappings. That contained md5-hashed strings and was not of any benefit (a separate logger for Polylang will come soon anyway).
173
+ - Fix notice in theme logger because did not check if `$_POST['sidebar']` was set. Fixes https://github.com/bonny/WordPress-Simple-History/issues/136.
174
+ - Fix thumbnail title missing notice in post logger.
175
+ - Fix PHP warning when a plugin was checked by WordPress for an update, but your WordPress install did not have the plugin folder for that plugin.
176
+ - Fix unexpected single-quotations included in file name in Internet Explorer 11 (and possibly other versions) when exporting CSV/JSON file.
177
+ - Fix filter/search log by specific users not working. Fixes https://wordpress.org/support/topic/show-activity-from-other-authors-only/.
178
+ - Fix a notice in SimpleOptionsLogger.
179
+ - Better CSS styling on dashboard.
180
+ - Add filter `simple_history/post_logger/post_updated/context` that can be used to modify the context added by SimplePostLogger.
181
+ - Add filter `simple_history/post_logger/post_updated/ok_to_log` that can be used to skip logging a post update.
182
+ - Add filter `simple_history/categories_logger/skip_taxonomies` that can be used to modify what taxonomies to skip when logging updates to taxonomy terms.
183
+
184
  = 2.20 (November 2017) =
185
 
186
  - Add logging of post thumbnails.
189
  - Update Select2 to latest version. Fixes https://wordpress.org/support/topic/select2-js-is-outdated/.
190
  - Show a message if user is running to old WordPress version, and don't continue running the code of this plugin.
191
  Should fix stuff like https://wordpress.org/support/topic/simple-history-i-cannot-login/.
192
+ - Fix an error with PHP 7.1.
193
 
194
  = 2.19 (November 2017) =
195
 
templates/settings-style-example.php CHANGED
@@ -1,5 +1,11 @@
1
  <?php
2
- defined( 'ABSPATH' ) or die();
 
 
 
 
 
 
3
  ?>
4
 
5
  <div class="SimpleHistoryGuiExample">
@@ -23,7 +29,7 @@ defined( 'ABSPATH' ) or die();
23
  </div>
24
 
25
  <div class="SimpleHistoryLogitem__text">
26
- Short message descriping the thing that happened.
27
  </div>
28
 
29
  <div class="SimpleHistoryLogitem__details">
@@ -31,7 +37,7 @@ defined( 'ABSPATH' ) or die();
31
  <p>More information about the event goes here. Add links, tables, text, lists, etc.</p>
32
 
33
  <p>Some build in styles you can use:</p>
34
-
35
  <p>
36
  <a href="http://playground-root.ep/wp-admin/post.php?post=25097&amp;action=edit&amp;lang=en">
37
  <div class="SimpleHistoryLogitemThumbnail">
@@ -43,16 +49,16 @@ defined( 'ABSPATH' ) or die();
43
  <p>The <code>inlineDivided</code> class is used to group short pieces of information together, for example meta data:</p>
44
 
45
  <p>
46
- <span class="SimpleHistoryLogitem__inlineDivided">34 kB</span>
47
- <span class="SimpleHistoryLogitem__inlineDivided">PNG</span>
48
  <span class="SimpleHistoryLogitem__inlineDivided">420 × 420</span>
49
  </p>
50
 
51
  <p>
52
- <span class="SimpleHistoryLogitem__inlineDivided"><em>Filesize</em> 34 kB</span>
53
- <span class="SimpleHistoryLogitem__inlineDivided"><em>Format</em> PNG</span>
54
  <span class="SimpleHistoryLogitem__inlineDivided"><em>Dimensions</em> 420 × 420</span>
55
- </p>
56
 
57
  <p>Tables can be used if you have more data to show, like the meta data for a plugin:</p>
58
 
@@ -93,7 +99,7 @@ defined( 'ABSPATH' ) or die();
93
 
94
  <p>
95
  <span class="SimpleHistoryLogitem__inlineDivided">
96
- <em>Author:</em>
97
  <a href="http://bbpress.org">The bbPress Community</a>
98
  </span>
99
 
@@ -131,7 +137,7 @@ defined( 'ABSPATH' ) or die();
131
  </li>
132
 
133
  <?php
134
- // All debug levels
135
  $template = '
136
  <li class="SimpleHistoryLogitem SimpleHistoryLogitem--loglevel-%1$s SimpleHistoryLogitem--logger-SimpleMediaLogger SimpleHistoryLogitem--initiator-wp_user">
137
 
1
  <?php
2
+ /**
3
+ * Style example.
4
+ *
5
+ * @package SimpleHistory
6
+ */
7
+
8
+ defined( 'ABSPATH' ) || die();
9
  ?>
10
 
11
  <div class="SimpleHistoryGuiExample">
29
  </div>
30
 
31
  <div class="SimpleHistoryLogitem__text">
32
+ Short message descriping the thing that happened.
33
  </div>
34
 
35
  <div class="SimpleHistoryLogitem__details">
37
  <p>More information about the event goes here. Add links, tables, text, lists, etc.</p>
38
 
39
  <p>Some build in styles you can use:</p>
40
+
41
  <p>
42
  <a href="http://playground-root.ep/wp-admin/post.php?post=25097&amp;action=edit&amp;lang=en">
43
  <div class="SimpleHistoryLogitemThumbnail">
49
  <p>The <code>inlineDivided</code> class is used to group short pieces of information together, for example meta data:</p>
50
 
51
  <p>
52
+ <span class="SimpleHistoryLogitem__inlineDivided">34 kB</span>
53
+ <span class="SimpleHistoryLogitem__inlineDivided">PNG</span>
54
  <span class="SimpleHistoryLogitem__inlineDivided">420 × 420</span>
55
  </p>
56
 
57
  <p>
58
+ <span class="SimpleHistoryLogitem__inlineDivided"><em>Filesize</em> 34 kB</span>
59
+ <span class="SimpleHistoryLogitem__inlineDivided"><em>Format</em> PNG</span>
60
  <span class="SimpleHistoryLogitem__inlineDivided"><em>Dimensions</em> 420 × 420</span>
61
+ </p>
62
 
63
  <p>Tables can be used if you have more data to show, like the meta data for a plugin:</p>
64
 
99
 
100
  <p>
101
  <span class="SimpleHistoryLogitem__inlineDivided">
102
+ <em>Author:</em>
103
  <a href="http://bbpress.org">The bbPress Community</a>
104
  </span>
105
 
137
  </li>
138
 
139
  <?php
140
+ // All debug levels.
141
  $template = '
142
  <li class="SimpleHistoryLogitem SimpleHistoryLogitem--loglevel-%1$s SimpleHistoryLogitem--logger-SimpleMediaLogger SimpleHistoryLogitem--initiator-wp_user">
143