-
Notifications
You must be signed in to change notification settings - Fork 25
/
change-tracker.php
295 lines (238 loc) · 8.53 KB
/
change-tracker.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
<?php
/**
Plugin Name: WP Document Revisions - Track Changes to Document Metadata
Plugin URI:
Description: Auto-generates and appends revision summaries for changes to taxonomies, title, and visibility
Version: 0.1
Author: Benjamin J. Balter
Author URI: http://ben.balter.com
License: GPL2
*
* @package WP Document Revisions Code Cookbook
*/
/**
* Main WP_Document_Revisions Track class.
*/
class WPDR_Track_Meta_Changes {
/**
* Change List.
*
* @var array $document_change_list
*/
public $document_change_list = array();
/**
* WPDR Class.
*
* @var object Class
*/
public $wpdr;
/**
* Construct
*/
public function __construct() {
// set up class.
add_action( 'plugins_loaded', array( &$this, 'setup_wpdr' ) );
// taxs.
add_action( 'set_object_terms', array( &$this, 'build_taxonomy_change_list' ), 10, 6 );
// status.
add_action( 'transition_post_status', array( &$this, 'track_status_changes' ), 10, 3 );
// title.
add_action( 'save_post_document', array( &$this, 'track_title_changes' ), 10, 1 );
// appending.
add_action( 'save_post_document', array( &$this, 'append_changes_to_revision_summary' ), 20, 1 );
}
/**
* Makes all WPDR functions accessible as $this->wpdr->{function}
* Call here so that Doc Revs is loaded
*/
public function setup_wpdr() {
// ensure WP Document Revisions is loaded.
if ( ! class_exists( 'WP_Document_Revisions' ) ) {
return;
}
$this->wpdr = WP_Document_Revisions::$instance;
}
/**
* Compares post title to previous revisions post title and adds to internal array if changed
*
* @param int $post_ID the id of the post to check.
*/
public function track_title_changes( $post_ID ) {
if ( $this->dont_track( $post_ID ) ) {
return false;
}
$new = get_post( $post_ID );
$revisions = $this->wpdr->get_revisions( $post_ID );
// because we've already saved, [0] = this one, [1] = the previous one.
$old = $revisions[1];
if ( $new->post_title === $old->post_title ) {
return;
}
do_action( 'document_title_changed', $post_ID, $old, $new );
// translators: %1$s is the old title, %2$s is the new title.
$this->document_change_list[] = sprintf( __( 'Title changed from "%1$s" to "%2$s"', 'wp-document-revisions' ), $old->post_title, $new->post_title );
}
/**
* Tracks when a post status changes
*
* @param string $new_s the new status.
* @param string $old_s the old status.
* @param object $post the post object.
*/
public function track_status_changes( $new_s, $old_s, $post ) {
if ( $this->dont_track( $post->ID ) ) {
return false;
}
if ( 'new' === $old_s || 'auto_draft' === $old_s ) {
return false;
}
if ( $new_s === $old_s ) {
return false;
}
do_action( 'document_visibility_changed', $post->ID, $old_s, $new_s );
// translators: %1$s is the old status, %2$s is the new status.
$this->document_change_list[] = sprintf( __( 'Visibility changed from "%1$s" to "%2$s"', 'wp-document-revisions' ), $old_s, $new_s );
}
/**
* Tracks changes to taxonomies
*
* @param int $object_id the document ID.
* @param array $terms the new terms.
* @param array $tt_ids the new term IDs.
* @param string $taxonomy the taxonomy being changed.
* @param bool $append whether it is being appended or replaced.
* @param array $old_tt_ids term taxonomy ID array before the change.
*/
public function build_taxonomy_change_list( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
if ( $this->dont_track( $object_id ) ) {
return false;
}
// Added terms are specified terms, that did not already exist.
$added_tt_ids = array_diff( $tt_ids, $old_tt_ids );
if ( $append ) {
// If appending terms - nothing was removed.
$removed_tt_ids = array();
} else {
// Removed terms will be old terms, that were not specified in $tt_ids.
$removed_tt_ids = array_diff( $old_tt_ids, $tt_ids );
}
$taxonomy = get_taxonomy( $taxonomy );
// grab the proper taxonomy label.
$taxonomy_formatted = ( count( $added_tt_ids ) + count( $removed_tt_ids ) === 1 ) ? $taxonomy->labels->singular_name : $taxonomy->labels->name;
$add = '';
$sep = '';
$rem = '';
// Deal with added ones.
if ( ! empty( $added_tt_ids ) ) {
// These are taxonomy term IDs so need to get names from term_ids.
$terms_fmt = array();
foreach ( $added_tt_ids as $term ) {
$term_obj = get_term_by( 'term_taxonomy_id', $term, $taxonomy );
$terms_fmt[] = '"' . $term_obj->name . '"';
}
// human format the string by adding an "and" before the last term.
$last = array_pop( $terms_fmt );
if ( ! count( $terms_fmt ) ) {
$terms_formatted = $last;
} else {
$terms_formatted = implode( ', ', $terms_fmt ) . __( ' and ', 'wp-document-revisions' ) . $last;
}
// translators: %1$s is the list of terms added.
$add = sprintf( __( ' %1$s added', 'wp-document-revisions' ), $terms_formatted );
if ( ! empty( $removed_tt_ids ) ) {
// translators: separator between added and removed..
$sep = __( ',', 'wp-document-revisions' );
}
}
// Deal with removed ones.
if ( ! empty( $removed_tt_ids ) ) {
// These are taxonomy term IDs so need to get names from term_ids.
$terms_fmt = array();
foreach ( $removed_tt_ids as $term ) {
$term_obj = get_term_by( 'term_taxonomy_id', $term, $taxonomy );
$terms_fmt[] = '"' . $term_obj->name . '"';
}
// human format the string by adding an "and" before the last term.
$last = array_pop( $terms_fmt );
if ( ! count( $terms_fmt ) ) {
$terms_formatted = $last;
} else {
$terms_formatted = implode( ', ', $terms_fmt ) . __( ' and ', 'wp-document-revisions' ) . $last;
}
// translators: %1$s is the list of terms removed.
$rem = sprintf( __( ' %1$s removed', 'wp-document-revisions' ), $terms_formatted );
}
if ( '' !== $add || '' !== $rem ) {
// translators: %1$s is the taxonomy's name, %2$s is the list of terms added, %3$s is the separator, %3$s is the list of terms removed.
$message = sprintf( __( '%1$s:%2$s%3$s%4$s', 'wp-document-revisions' ), $taxonomy_formatted, $add, $sep, $rem );
$this->document_change_list[] = $message;
}
do_action( 'document_taxonomy_changed', $object_id, $taxonomy->name, $tt_ids, $old_tt_ids );
}
/**
* Loops through document change list and appends to latest revisions's log message
*
* @param int $post_ID the ID of the document being changed.
*/
public function append_changes_to_revision_summary( $post_ID ) {
global $wpdb;
if ( $this->dont_track( $post_ID ) ) {
return false;
}
if ( empty( $this->document_change_list ) ) {
return false;
}
$post = get_post( $post_ID );
$message = trim( $post->post_excerpt );
if ( ! empty( $message ) && ' ' !== substr( $message, -1, 1 ) ) {
$message .= ' ';
}
// escape HTML and implode list on semi-colons.
$change_list = esc_html( stripslashes( implode( '; ', $this->document_change_list ) ) );
$message .= '(' . $change_list . ')';
$message = apply_filters( 'document_revision_log_auto_append_message', $message, $post_ID );
// manually update the DB here so that we don't create another revision.
// phpcs:disable WordPress.DB.DirectDatabaseQuery
$wpdb->update( $wpdb->posts, array( 'post_excerpt' => $message ), array( 'ID' => $post_ID ), '%s', '%d' );
// need to clean the cache.
clean_post_cache( $post_ID );
$r = $wpdb->get_var(
$wpdb->prepare(
'SELECT MAX(ID) FROM ' . $wpdb->posts . " WHERE post_parent=%d AND post_type='revision';",
$post_ID
)
);
if ( ! is_null( $r ) ) {
$wpdb->update( $wpdb->posts, array( 'post_excerpt' => $message ), array( 'ID' => $r ), '%s', '%d' );
}
// phpcs:enable WordPress.DB.DirectDatabaseQuery
// clear WPDR revision cache.
wp_cache_delete( $post_ID, 'document_revisions' );
do_action( 'document_meta_change', $post_ID, $message );
// reset in case another post is also being saved for some reason.
$this->document_change_list = array();
}
/**
* Determines whether changes should be tracked for a given post
*
* @param int $post_ID the ID of the post.
* @returns bool true if shouldn't track, otherwise false
*/
private function dont_track( $post_ID ) {
if ( ! apply_filters( 'track_document_meta_changes', true, $post_ID ) ) {
return true;
}
if ( wp_is_post_revision( $post_ID ) ) {
return true;
}
if ( ! $this->wpdr->verify_post_type( $post_ID ) ) {
return true;
}
$revisions = $this->wpdr->get_revisions( $post_ID );
if ( count( $revisions ) <= 1 ) {
return true;
}
return false;
}
}
new WPDR_Track_Meta_Changes();