PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.1.0
Defender Security – Malware Scanner, Login Security & Firewall v3.1.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / audit / post-audit.php

post-audit.php in Defender Security – Malware Scanner, Login Security & Firewall 3.1.0, at src/component/audit/post-audit.php

433 lines 11.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Author: Hoang Ngo
4 */
5
6 namespace WP_Defender\Component\Audit;
7
8 use Calotes\Helper\Array_Cache;
9 use WP_Defender\Traits\User;
10 use WP_Defender\Model\Audit_Log;
11
12 class Post_Audit extends Audit_Event {
13 use User;
14
15 private $excluded_posttype = array(
16 'wdscan_result',
17 'wdf_scan',
18 'wd_iplockout_log',
19 'wd_ip_lockout',
20 'wdf_scan_item',
21 );
22
23 /**
24 * We will add a hook, for updated event, and cache that event content.
25 * Later we weill use the hook save post, to determine this is insert new post or update the cache will be
26 * the array of various post, as we don't want data be excluded. This way we can get more control.
27 */
28 public function __construct() {
29 add_action( 'post_updated', array( &$this, 'cache_post_updated' ), 10, 3 );
30 }
31
32 public function cache_post_updated( $post_id, $after, $before ) {
33 Array_Cache::append(
34 'post_updated',
35 array(
36 'post_id' => $post_id,
37 'after' => $after,
38 'before' => $before,
39 ),
40 'audit'
41 );
42 }
43
44 /**
45 * @return array
46 */
47 public function get_hooks() {
48 return array(
49 'save_post' => array(
50 'args' => array( 'post_ID', 'post', 'is_updated' ),
51 'callback' => array( self::class, 'post_updated_callback' ),
52 'event_type' => Audit_Log::EVENT_TYPE_CONTENT,
53 'action_type' => self::ACTION_UPDATED,
54 ),
55 'transition_post_status' => array(
56 'args' => array( 'new_status', 'old_status', 'post' ),
57 'event_type' => Audit_Log::EVENT_TYPE_CONTENT,
58 'action_type' => self::ACTION_UPDATED,
59 'false_when' => array(
60 array(
61 '{{post->post_status}}',
62 array(
63 'inherit',
64 'new',
65 'auto-draft',
66 'trash',
67 ),
68 ),
69 array(
70 '{{post->post_type}}',
71 array_merge( array( 'revision', 'nav_menu_item' ), $this->excluded_posttype ),
72 ),
73 array(
74 '{{new_status}}',
75 '{{old_status}}',
76 ),
77 array(
78 '{{old_status}}',
79 array(
80 'trash',
81 'new',
82 ),
83 ),
84 ),
85 'text' => array(
86 array(
87 sprintf(
88 /* translators: */
89 __( '%1$s %2$s published %3$s "%4$s"', 'wpdef' ),
90 '{{blog_name}}',
91 '{{wp_user}}',
92 '{{post_type_label}}',
93 '{{post_title}}'
94 ),
95 '{{new_status}}',
96 'publish',
97 '==',
98 ),
99 array(
100 sprintf(
101 /* translators: */
102 __( '%1$s %2$s pending %3$s "%4$s"', 'wpdef' ),
103 '{{blog_name}}',
104 '{{wp_user}}',
105 '{{post_type_label}}',
106 '{{post_title}}'
107 ),
108 '{{new_status}}',
109 'pending',
110 '==',
111 ),
112 array(
113 sprintf(
114 /* translators: */
115 __( '%1$s %2$s drafted %3$s "%4$s"', 'wpdef' ),
116 '{{blog_name}}',
117 '{{wp_user}}',
118 '{{post_type_label}}',
119 '{{post_title}}'
120 ),
121 '{{new_status}}',
122 'draft',
123 '==',
124 ),
125 array(
126 sprintf(
127 /* translators: */
128 __( '%1$s %2$s changed %3$s "%4$s" status from %5$s to %6$s', 'wpdef' ),
129 '{{blog_name}}',
130 '{{wp_user}}',
131 '{{post_type_label}}',
132 '{{post_title}}',
133 '{{old_status}}',
134 '{{new_status}}'
135 ),
136 '{{new_status}}',
137 '{{new_status}}',
138 '==',
139 ),
140 ),
141 'program_args' => array(
142 'post_type_label' => array(
143 'callable' => 'get_post_type_object',
144 'params' => array(
145 '{{post->post_type}}',
146 ),
147 'result_property' => 'labels->singular_name',
148 ),
149 ),
150 'custom_args' => array(
151 'post_title' => '{{post->post_title}}',
152 ),
153 'context' => '{{post_type_label}}',
154 ),
155 'delete_post' => array(
156 'args' => array( 'post_ID' ),
157 'event_type' => Audit_Log::EVENT_TYPE_CONTENT,
158 'action_type' => self::ACTION_DELETED,
159 'text' => sprintf(
160 /* translators: */
161 __( '%1$s %2$s deleted %3$s "%4$s"', 'wpdef' ),
162 '{{blog_name}}',
163 '{{wp_user}}',
164 '{{post_type_label}}',
165 '{{post_title}}'
166 ),
167 'program_args' => array(
168 'post' => array(
169 'callable' => 'get_post',
170 'params' => array(
171 '{{post_ID}}',
172 ),
173 ),
174 'post_type_label' => array(
175 'callable' => 'get_post_type_object',
176 'params' => array(
177 '{{post->post_type}}',
178 ),
179 'result_property' => 'labels->singular_name',
180 ),
181 'post_title' => array(
182 'callable' => 'get_post',
183 'params' => array(
184 '{{post_ID}}',
185 ),
186 'result_property' => 'post_title',
187 ),
188 ),
189 'context' => '{{post_type_label}}',
190 'false_when' => array(
191 array(
192 '{{post->post_type}}',
193 array_merge(
194 array(
195 'revision',
196 'attachment',
197 'nav_menu_item',
198 ),
199 $this->excluded_posttype
200 ),
201 ),
202 array(
203 '{{post_type_label}}',
204 '',
205 ),
206 ),
207 ),
208 'untrashed_post' => array(
209 'args' => array( 'post_ID' ),
210 'action_type' => self::ACTION_RESTORED,
211 'event_type' => Audit_Log::EVENT_TYPE_CONTENT,
212 'text' => sprintf(
213 /* translators: */
214 __( '%1$s %2$s untrashed %3$s "%4$s"', 'wpdef' ),
215 '{{blog_name}}',
216 '{{wp_user}}',
217 '{{post_type_label}}',
218 '{{post_title}}'
219 ),
220 'program_args' => array(
221 'post' => array(
222 'callable' => 'get_post',
223 'params' => array(
224 '{{post_ID}}',
225 ),
226 ),
227 'post_type_label' => array(
228 'callable' => 'get_post_type_object',
229 'params' => array(
230 '{{post->post_type}}',
231 ),
232 'result_property' => 'labels->singular_name',
233 ),
234 'post_title' => array(
235 'callable' => 'get_post',
236 'params' => array(
237 '{{post_ID}}',
238 ),
239 'result_property' => 'post_title',
240 ),
241 ),
242 'context' => '{{post_type_label}}',
243 ),
244 'trashed_post' => array(
245 'args' => array( 'post_ID' ),
246 'action_type' => self::ACTION_TRASHED,
247 'event_type' => Audit_Log::EVENT_TYPE_CONTENT,
248 'text' => sprintf(
249 /* translators: */
250 __( '%1$s %2$s trashed %3$s "%4$s"', 'wpdef' ),
251 '{{blog_name}}',
252 '{{wp_user}}',
253 '{{post_type_label}}',
254 '{{post_title}}'
255 ),
256 'program_args' => array(
257 'post' => array(
258 'callable' => 'get_post',
259 'params' => array(
260 '{{post_ID}}',
261 ),
262 ),
263 'post_type_label' => array(
264 'callable' => 'get_post_type_object',
265 'params' => array(
266 '{{post->post_type}}',
267 ),
268 'result_property' => 'labels->singular_name',
269 ),
270 'post_title' => array(
271 'callable' => 'get_post',
272 'params' => array(
273 '{{post_ID}}',
274 ),
275 'result_property' => 'post_title',
276 ),
277 ),
278 'context' => '{{post_type_label}}',
279 ),
280 );
281 }
282
283 public function post_updated_callback() {
284 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
285 return false;
286 }
287
288 $args = func_get_args();
289 $hookname = $args[0];
290 $post = $args[1]['post'];
291
292 if ( in_array(
293 $post->post_status,
294 array(
295 'trash',
296 'auto-draft',
297 ),
298 true
299 ) || in_array( $post->post_type, array( 'revision' ), true )
300 ) {
301 // Usually, wp wll append :trash to the post name, so this case we just return.
302 return false;
303 }
304
305 if ( in_array( $post->post_type, $this->excluded_posttype, true ) ) {
306 return false;
307 }
308
309 if ( 'nav_menu_item' === $post->post_type ) {
310 return false;
311 }
312
313 $post_type = get_post_type_object( $post->post_type );
314 if ( ! is_object( $post_type ) ) {
315 return false;
316 }
317
318 $is_updated = $args[1]['is_updated'];
319 $post_before = null;
320 $cached = Array_Cache::get( 'post_updated', 'audit', array() );
321 foreach ( $cached as $post_arr ) {
322 if ( $post->ID === $post_arr['post_id'] ) {
323 $post_before = $post_arr['before'];
324 break;
325 }
326 }
327
328 $blog_name = is_multisite() ? '[' . get_bloginfo( 'name' ) . ']' : '';
329 if ( true === $is_updated ) {
330 if ( ! is_null( $post_before ) ) {
331 $post_after = $post->to_array();
332 $post_before = $post_before->to_array();
333
334 // Status transitions are handled via other hooks.
335 if ( $post_before['post_status'] !== $post_after['post_status'] ) {
336 return false;
337 }
338
339 // Unset the date modified, and post status, as we got another hook for that.
340 unset( $post_after['post_modified'] );
341 unset( $post_after['post_modified_gmt'] );
342 unset( $post_after['post_status'] );
343 unset( $post_before['post_modified'] );
344 unset( $post_before['post_modified_gmt'] );
345 unset( $post_before['post_status'] );
346 if ( serialize( $post_before ) != serialize( $post_after ) ) {
347 $item_changed_count = count( self::array_recursive_diff( $post_before, $post_after ) );
348 if ( $post_before['post_title'] !== $post_after['post_title'] && 1 === $item_changed_count ) {
349 $text = sprintf(
350 /* translators: */
351 __( '%1$s %2$s updated %3$s ID %4$d, title from "%5$s" to "%6$s"', 'wpdef' ),
352 $blog_name,
353 $this->get_user_display( get_current_user_id() ),
354 $post_type->labels->singular_name,
355 $post_after['ID'],
356 $post_before['post_title'],
357 $post_after['post_title']
358 );
359 } elseif ( $post_before['post_name'] !== $post_after['post_name'] && 1 === $item_changed_count ) {
360 $text = sprintf(
361 /* translators: */
362 __( '%1$s %2$s updated %3$s ID %4$d, slug from "%5$s" to "%6$s"', 'wpdef' ),
363 $blog_name,
364 $this->get_user_display( get_current_user_id() ),
365 $post_type->labels->singular_name,
366 $post_after['ID'],
367 $post_before['post_name'],
368 $post_after['post_name']
369 );
370 } elseif ( $post_before['post_author'] !== $post_after['post_author'] && 1 === $item_changed_count ) {
371 $text = sprintf(
372 /* translators: */
373 __( '%1$s %2$s updated %3$s ID %4$d, author from "%5$s" to "%6$s"', 'wpdef' ),
374 $blog_name,
375 $this->get_user_display( get_current_user_id() ),
376 $post_type->labels->singular_name,
377 $post_after['ID'],
378 $this->get_user_display( $post_before['post_author'] ),
379 $this->get_user_display( $post_after['post_author'] )
380 );
381 } else {
382 $text = sprintf(
383 /* translators: */
384 __( '%1$s %2$s updated %3$s "%4$s"', 'wpdef' ),
385 $blog_name,
386 $this->get_user_display( get_current_user_id() ),
387 $post_type->labels->singular_name,
388 $post_after['post_title']
389 );
390 }
391
392 return array(
393 $text,
394 $post_type->labels->singular_name,
395 );
396 }
397 }
398 } else {
399 if ( is_null( $post_before ) ) {
400 $text = sprintf(
401 /* translators: */
402 __( '%1$s %2$s added new %3$s "%4$s"', 'wpdef' ),
403 $blog_name,
404 $this->get_user_display( get_current_user_id() ),
405 $post_type->labels->singular_name,
406 $post->post_title
407 );
408
409 return array(
410 $text,
411 $post_type->labels->singular_name,
412 );
413 }
414 }
415
416 return false;
417 }
418
419 public static function compare_post( $a, $b ) {
420 if ( ! is_array( $a ) && ! is_array( $b ) ) {
421 return strcmp( $a, $b ) === 0;
422 } elseif ( is_array( $a ) && is_array( $b ) ) {
423 return count( array_diff( $a, $b ) ) === 0;
424 } else {
425 return 0;
426 }
427 }
428
429 public function dictionary() {
430 return array();
431 }
432 }
433