PluginProbe ʕ •ᴥ•ʔ
WP-Sweep / 1.0.7
WP-Sweep v1.0.7
2.0.1 2.0.0 1.2.0 trunk 1.0.10 1.0.11 1.0.12 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9
wp-sweep / wp-sweep.php
wp-sweep Last commit date
js 11 years ago admin.php 10 years ago composer.json 10 years ago index.php 11 years ago readme.txt 10 years ago uninstall.php 11 years ago wp-sweep.php 10 years ago
wp-sweep.php
896 lines
1 <?php
2 /*
3 Plugin Name: WP-Sweep
4 Plugin URI: http://lesterchan.net/portfolio/programming/php/
5 Description: WP-Sweep allows you to clean up unused, orphaned and duplicated data in your WordPress. It cleans up revisions, auto drafts, unapproved comments, spam comments, trashed comments, orphan post meta, orphan comment meta, orphan user meta, orphan term relationships, unused terms, duplicated post meta, duplicated comment meta, duplicated user meta and transient options. It also optimizes your database tables.
6 Version: 1.0.7
7 Author: Lester 'GaMerZ' Chan
8 Author URI: http://lesterchan.net
9 Text Domain: wp-sweep
10 License: GPL2
11 */
12
13 /* Copyright 2016 Lester Chan (email : lesterchan@gmail.com)
14
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License, version 2, as
17 published by the Free Software Foundation.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 */
28
29 /**
30 * WP-Sweep version
31 *
32 * @since 1.0.0
33 */
34 define( 'WP_SWEEP_VERSION', '1.0.6' );
35
36 /**
37 * WP-Sweep class
38 *
39 * @since 1.0.0
40 */
41 class WPSweep {
42 /**
43 * Limit the number of items to show for sweep details
44 *
45 * @since 1.0.3
46 *
47 * @access public
48 * @var int
49 */
50 public $limit_details = 500;
51
52 /**
53 * Static instance
54 *
55 * @since 1.0.0
56 *
57 * @access private
58 * @var $instance
59 */
60 private static $instance;
61
62 /**
63 * Constructor method
64 *
65 * @since 1.0.0
66 *
67 * @access public
68 */
69 public function __construct() {
70 // Add Plugin Hooks
71 add_action( 'plugins_loaded', array( $this, 'add_hooks' ) );
72
73 // Load Translation
74 load_plugin_textdomain( 'wp-sweep' );
75
76 // Plugin Activation/Deactivation
77 register_activation_hook( __FILE__, array( $this, 'plugin_activation' ) );
78 register_deactivation_hook( __FILE__, array( $this, 'plugin_deactivation' ) );
79 }
80
81 /**
82 * Initializes the plugin object and returns its instance
83 *
84 * @since 1.0.0
85 *
86 * @access public
87 * @return object The plugin object instance
88 */
89 public static function get_instance() {
90 if ( ! isset( self::$instance ) ) {
91 self::$instance = new self();
92 }
93 return self::$instance;
94 }
95
96 /**
97 * Init this plugin
98 *
99 * @since 1.0.0
100 *
101 * @access public
102 * @return void
103 */
104 public function init() {}
105
106 /**
107 * Adds all the plugin hooks
108 *
109 * @since 1.0.0
110 *
111 * @access public
112 * @return void
113 */
114 public function add_hooks() {
115 // Actions
116 add_action( 'init', array( $this, 'init' ) );
117 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
118 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
119 add_action( 'wp_ajax_sweep_details', array( $this, 'ajax_sweep_details' ) );
120 add_action( 'wp_ajax_sweep', array( $this, 'ajax_sweep' ) );
121 }
122
123 /**
124 * Enqueue JS/CSS files used for admin
125 *
126 * @since 1.0.3
127 *
128 * @access public
129 * @param string $hook
130 * @return void
131 */
132 public function admin_enqueue_scripts( $hook ) {
133 if( 'wp-sweep/admin.php' !== $hook ) {
134 return;
135 }
136
137 if( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
138 wp_enqueue_script( 'wp-sweep', plugins_url( 'wp-sweep/js/wp-sweep.js' ), array( 'jquery' ) , WP_SWEEP_VERSION, true );
139 } else {
140 wp_enqueue_script( 'wp-sweep', plugins_url( 'wp-sweep/js/wp-sweep.min.js' ), array( 'jquery' ) , WP_SWEEP_VERSION, true );
141 }
142
143 wp_localize_script( 'wp-sweep', 'wp_sweep', array(
144 'text_close_warning' => __( 'Sweeping is in progress. If you leave now, the process won\'t be completed.', 'wp-sweep' ),
145 'text_sweep' => __( 'Sweep', 'wp-sweep' ),
146 'text_sweep_all' => __( 'Sweep All', 'wp-sweep' ),
147 'text_sweeping' => __( 'Sweeping ...', 'wp-sweep' ),
148 'text_na' => __( 'N/A', 'wp-sweep' )
149 ) );
150 }
151
152 /**
153 * Admin menu
154 *
155 * @since 1.0.3
156 *
157 * @access public
158 * @return void
159 */
160 public function admin_menu() {
161 add_management_page( __( 'Sweep', 'wp-sweep' ), __( 'Sweep', 'wp-sweep' ), 'activate_plugins', 'wp-sweep/admin.php' );
162 }
163
164
165 /**
166 * Sweep Details loaded via AJAX
167 *
168 * @since 1.0.3
169 *
170 * @access public
171 * @return void
172 */
173 public function ajax_sweep_details() {
174 if( ! empty( $_GET['action'] ) && $_GET['action'] === 'sweep_details' && ! empty( $_GET['sweep_name'] ) && ! empty( $_GET['sweep_type'] ) ) {
175 // Verify Referer
176 if ( ! check_admin_referer( 'wp_sweep_details_' . $_GET['sweep_name'] ) ) {
177 wp_send_json_error( array(
178 'error' => __( 'Failed to verify referrer.', 'wp-sweep' )
179 ) );
180 } else {
181 wp_send_json_success( $this->details( $_GET['sweep_name'] ) );
182 }
183 }
184 }
185
186 /**
187 * Sweep via AJAX
188 *
189 * @since 1.0.3
190 *
191 * @access public
192 * @return void
193 */
194 public function ajax_sweep() {
195 if( ! empty( $_GET['action'] ) && $_GET['action'] === 'sweep' && ! empty( $_GET['sweep_name'] ) && ! empty( $_GET['sweep_type'] ) ) {
196 // Verify Referer
197 if ( ! check_admin_referer( 'wp_sweep_' . $_GET['sweep_name'] ) ) {
198 wp_send_json_error( array(
199 'error' => __( 'Failed to verify referrer.', 'wp-sweep' )
200 ) );
201 } else {
202 $sweep = $this->sweep( $_GET['sweep_name'] );
203 $count = $this->count( $_GET['sweep_name'] );
204 $total_count = $this->total_count( $_GET['sweep_type'] );
205 $total_stats = array();
206 switch( $_GET['sweep_type'] ) {
207 case 'posts':
208 case 'postmeta':
209 $total_stats = array( 'posts' => $this->total_count( 'posts' ), 'postmeta' => $this->total_count( 'postmeta' ) );
210 break;
211 case 'comments':
212 case 'commentmeta':
213 $total_stats = array( 'comments' => $this->total_count( 'comments' ), 'commentmeta' => $this->total_count( 'commentmeta' ) );
214 break;
215 case 'users':
216 case 'usermeta':
217 $total_stats = array( 'users' => $this->total_count( 'users' ), 'usermeta' => $this->total_count( 'usermeta' ) );
218 break;
219 case 'term_relationships':
220 case 'term_taxonomy':
221 case 'terms':
222 case 'termmeta':
223 $total_stats = array( 'term_relationships' => $this->total_count( 'term_relationships' ), 'term_taxonomy' => $this->total_count( 'term_taxonomy' ), 'terms' => $this->total_count( 'terms' ), 'termmeta' => $this->total_count( 'termmeta' ) );
224 break;
225 case 'options':
226 $total_stats = array( 'options' => $this->total_count( 'options' ) );
227 break;
228 case 'tables':
229 $total_stats = array( 'tables' => $this->total_count( 'tables' ) );
230 break;
231 }
232
233 wp_send_json_success( array(
234 'sweep' => $sweep,
235 'count' => $count,
236 'total' => $total_count,
237 'percentage' => $this->format_percentage( $count, $total_count ),
238 'stats' => $total_stats
239 ) );
240 }
241 }
242 }
243
244 /**
245 * Count the number of total items belonging to each sweep
246 *
247 * @since 1.0.0
248 *
249 * @access public
250 * @param string $name
251 * @return int Number of items belonging to each sweep
252 */
253 public function total_count( $name ) {
254 global $wpdb;
255
256 $count = 0;
257
258 switch( $name ) {
259 case 'posts':
260 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts" );
261 break;
262 case 'postmeta':
263 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->postmeta" );
264 break;
265 case 'comments':
266 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments" );
267 break;
268 case 'commentmeta':
269 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->commentmeta" );
270 break;
271 case 'users':
272 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users" );
273 break;
274 case 'usermeta':
275 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta" );
276 break;
277 case 'term_relationships':
278 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
279 break;
280 case 'term_taxonomy':
281 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
282 break;
283 case 'terms':
284 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
285 break;
286 case 'termmeta':
287 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->termmeta" );
288 break;
289 case 'options':
290 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->options" );
291 break;
292 case 'tables':
293 $count = sizeof( $wpdb->get_col( 'SHOW TABLES' ) );
294 break;
295 }
296
297 return apply_filters( 'wp_sweep_total_count', $count, $name );
298 }
299
300 /**
301 * Count the number of items belonging to each sweep
302 *
303 * @since 1.0.0
304 *
305 * @access public
306 * @param string $name
307 * @return int Number of items belonging to each sweep
308 */
309 public function count( $name ) {
310 global $wpdb;
311
312 $count = 0;
313
314 switch( $name ) {
315 case 'revisions':
316 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = %s", 'revision' ) );
317 break;
318 case 'auto_drafts':
319 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = %s", 'auto-draft' ) );
320 break;
321 case 'deleted_posts':
322 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = %s", 'trash' ) );
323 break;
324 case 'unapproved_comments':
325 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = %s", '0' ) );
326 break;
327 case 'spam_comments':
328 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = %s", 'spam' ) );
329 break;
330 case 'deleted_comments':
331 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s)", 'trash', 'post-trashed' ) );
332 break;
333 case 'transient_options':
334 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(option_id) FROM $wpdb->options WHERE option_name LIKE(%s)", '%_transient_%' ) );
335 break;
336 case 'orphan_postmeta':
337 $count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts)" );
338 break;
339 case 'orphan_commentmeta':
340 $count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)" );
341 break;
342 case 'orphan_usermeta':
343 $count = $wpdb->get_var( "SELECT COUNT(umeta_id) FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)" );
344 break;
345 case 'orphan_termmeta':
346 $count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)" );
347 break;
348 case 'orphan_term_relationships':
349 $count = $wpdb->get_var( "SELECT COUNT(object_id) FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy != 'link_category' AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts)" );
350 break;
351 case 'unused_terms':
352 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(t.term_id) FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.count = %d AND t.term_id NOT IN (" . implode( ',', $this->get_excluded_termids() ) . ")", 0 ) );
353 break;
354 case 'duplicated_postmeta':
355 $query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(meta_id) AS count FROM $wpdb->postmeta GROUP BY post_id, meta_key, meta_value HAVING count > %d", 1 ) );
356 if( is_array( $query ) ) {
357 $count = array_sum( array_map( 'intval', $query ) );
358 }
359 break;
360 case 'duplicated_commentmeta':
361 $query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(meta_id) AS count FROM $wpdb->commentmeta GROUP BY comment_id, meta_key, meta_value HAVING count > %d", 1 ) );
362 if( is_array( $query ) ) {
363 $count = array_sum( array_map( 'intval', $query ) );
364 }
365 break;
366 case 'duplicated_usermeta':
367 $query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(umeta_id) AS count FROM $wpdb->usermeta GROUP BY user_id, meta_key, meta_value HAVING count > %d", 1 ) );
368 if( is_array( $query ) ) {
369 $count = array_sum( array_map( 'intval', $query ) );
370 }
371 break;
372 case 'duplicated_termmeta':
373 $query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(meta_id) AS count FROM $wpdb->termmeta GROUP BY term_id, meta_key, meta_value HAVING count > %d", 1 ) );
374 if( is_array( $query ) ) {
375 $count = array_sum( array_map( 'intval', $query ) );
376 }
377 break;
378 case 'optimize_database':
379 $count = sizeof( $wpdb->get_col( 'SHOW TABLES' ) );
380 break;
381 case 'oembed_postmeta':
382 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(meta_id) FROM $wpdb->postmeta WHERE meta_key LIKE(%s)", '%_oembed_%' ) );
383 break;
384 }
385
386 return apply_filters( 'wp_sweep_count', $count, $name );
387 }
388
389 /**
390 * Return more details about a sweep
391 *
392 * @since 1.0.3
393 *
394 * @access public
395 * @param string $name
396 * @return int Number of items belonging to each sweep
397 */
398 public function details( $name ) {
399 global $wpdb;
400
401 $details = array();
402
403 switch( $name ) {
404 case 'revisions':
405 $details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_type = %s LIMIT %d", 'revision', $this->limit_details ) );
406 break;
407 case 'auto_drafts':
408 $details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_status = %s LIMIT %d", 'auto-draft', $this->limit_details ) );
409 break;
410 case 'deleted_posts':
411 $details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_status = %s LIMIT %d", 'trash', $this->limit_details ) );
412 break;
413 case 'unapproved_comments':
414 $details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE comment_approved = %s LIMIT %d", '0', $this->limit_details ) );
415 break;
416 case 'spam_comments':
417 $details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE comment_approved = %s LIMIT %d", 'spam', $this->limit_details ) );
418 break;
419 case 'deleted_comments':
420 $details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s) LIMIT %d", 'trash', 'post-trashed', $this->limit_details ) );
421 break;
422 case 'transient_options':
423 $details = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE(%s) LIMIT %d", '%_transient_%', $this->limit_details ) );
424 break;
425 case 'orphan_postmeta':
426 $details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts) LIMIT %d", $this->limit_details ) );
427 break;
428 case 'orphan_commentmeta':
429 $details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments) LIMIT %d", $this->limit_details ) );
430 break;
431 case 'orphan_usermeta':
432 $details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users) LIMIT %d", $this->limit_details ) );
433 break;
434 case 'orphan_termmeta':
435 $details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms) LIMIT %d", $this->limit_details ) );
436 break;
437 case 'orphan_term_relationships':
438 $details = $wpdb->get_col( $wpdb->prepare( "SELECT tt.taxonomy FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy != 'link_category' AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts) LIMIT %d", $this->limit_details ) );
439 break;
440 case 'unused_terms':
441 $details = $wpdb->get_col( $wpdb->prepare( "SELECT t.name FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.count = %d AND t.term_id NOT IN (" . implode( ',', $this->get_excluded_termids() ) . ") LIMIT %d", 0, $this->limit_details ) );
442 break;
443 case 'duplicated_postmeta':
444 $query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(meta_id) AS count, meta_key FROM $wpdb->postmeta GROUP BY post_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
445 $details = array();
446 if( $query ) {
447 foreach( $query as $meta ) {
448 $details[] = $meta->meta_key;
449 }
450 }
451 break;
452 case 'duplicated_commentmeta':
453 $query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(meta_id) AS count, meta_key FROM $wpdb->commentmeta GROUP BY comment_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
454 $details = array();
455 if( $query ) {
456 foreach( $query as $meta ) {
457 $details[] = $meta->meta_key;
458 }
459 }
460 break;
461 case 'duplicated_usermeta':
462 $query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(umeta_id) AS count, meta_key FROM $wpdb->usermeta GROUP BY user_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
463 $details = array();
464 if( $query ) {
465 foreach( $query as $meta ) {
466 $details[] = $meta->meta_key;
467 }
468 }
469 break;
470 case 'duplicated_termmeta':
471 $query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(meta_id) AS count, meta_key FROM $wpdb->termmeta GROUP BY term_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
472 $details = array();
473 if( $query ) {
474 foreach( $query as $meta ) {
475 $details[] = $meta->meta_key;
476 }
477 }
478 break;
479 case 'optimize_database':
480 $details = $wpdb->get_col( 'SHOW TABLES' );
481 break;
482 case 'oembed_postmeta':
483 $details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->postmeta WHERE meta_key LIKE(%s) LIMIT %d", '%_oembed_%', $this->limit_details ) );
484 break;
485 }
486
487 return apply_filters( 'wp_sweep_details', $details, $name );
488 }
489
490 /**
491 * Does the sweeping/cleaning up
492 *
493 * @since 1.0.0
494 *
495 * @access public
496 * @param string $name
497 * @return string Processed message
498 */
499 public function sweep( $name ) {
500 global $wpdb;
501
502 $message = '';
503
504 switch( $name ) {
505 case 'revisions':
506 $query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = %s", 'revision' ) );
507 if( $query ) {
508 foreach ( $query as $id ) {
509 wp_delete_post_revision( intval( $id ) );
510 }
511
512 $message = sprintf( __( '%s Revisions Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
513 }
514 break;
515 case 'auto_drafts':
516 $query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_status = %s", 'auto-draft' ) );
517 if( $query ) {
518 foreach ( $query as $id ) {
519 wp_delete_post( intval( $id ), true );
520 }
521
522 $message = sprintf( __( '%s Auto Drafts Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
523 }
524 break;
525 case 'deleted_posts':
526 $query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_status = %s", 'trash' ) );
527 if( $query ) {
528 foreach ( $query as $id ) {
529 wp_delete_post( $id, true );
530 }
531
532 $message = sprintf( __( '%s Deleted Posts Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
533 }
534 break;
535 case 'unapproved_comments':
536 $query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_approved = %s", '0' ) );
537 if( $query ) {
538 foreach ( $query as $id ) {
539 wp_delete_comment( intval( $id ), true );
540 }
541
542 $message = sprintf( __( '%s Unapproved Comments Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
543 }
544 break;
545 case 'spam_comments':
546 $query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_approved = %s", 'spam' ) );
547 if( $query ) {
548 foreach ( $query as $id ) {
549 wp_delete_comment( intval( $id ), true );
550 }
551
552 $message = sprintf( __( '%s Spam Comments Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
553 }
554 break;
555 case 'deleted_comments':
556 $query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s)", 'trash', 'post-trashed' ) );
557 if( $query ) {
558 foreach ( $query as $id ) {
559 wp_delete_comment( intval( $id ), true );
560 }
561
562 $message = sprintf( __( '%s Trash Comments Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
563 }
564 break;
565 case 'transient_options':
566 $query = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE(%s)", '%_transient_%' ) );
567 if( $query ) {
568 foreach ( $query as $option_name ) {
569 if( strpos( $option_name, '_site_transient_' ) !== false ) {
570 delete_site_transient( str_replace( '_site_transient_', '', $option_name ) );
571 } else {
572 delete_transient( str_replace( '_transient_', '', $option_name ) );
573 }
574 }
575
576 $message = sprintf( __( '%s Transient Options Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
577 }
578 break;
579 case 'orphan_postmeta':
580 $query = $wpdb->get_results( "SELECT post_id, meta_key FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts)" );
581 if( $query ) {
582 foreach ( $query as $meta ) {
583 $post_id = intval( $meta->post_id );
584 if( $post_id === 0 ) {
585 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta->meta_key ) );
586 } else {
587 delete_post_meta( $post_id, $meta->meta_key );
588 }
589 }
590
591 $message = sprintf( __( '%s Orphaned Post Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
592 }
593 break;
594 case 'orphan_commentmeta':
595 $query = $wpdb->get_results( "SELECT comment_id, meta_key FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)" );
596 if( $query ) {
597 foreach ( $query as $meta ) {
598 $comment_id = intval( $meta->comment_id );
599 if( $comment_id === 0 ) {
600 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->commentmeta WHERE comment_id = %d AND meta_key = %s", $comment_id, $meta->meta_key ) );
601 } else {
602 delete_comment_meta( $comment_id, $meta->meta_key );
603 }
604 }
605
606 $message = sprintf( __( '%s Orphaned Comment Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
607 }
608 break;
609 case 'orphan_usermeta':
610 $query = $wpdb->get_results( "SELECT user_id, meta_key FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)" );
611 if( $query ) {
612 foreach ( $query as $meta ) {
613 $user_id = intval( $meta->user_id );
614 if( $user_id === 0 ) {
615 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $user_id, $meta->meta_key ) );
616 } else {
617 delete_user_meta( $user_id, $meta->meta_key );
618 }
619 }
620
621 $message = sprintf( __( '%s Orphaned User Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
622 }
623 break;
624 case 'orphan_termmeta':
625 $query = $wpdb->get_results( "SELECT term_id, meta_key FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)" );
626 if( $query ) {
627 foreach ( $query as $meta ) {
628 $term_id = intval( $meta->term_id );
629 if( $term_id === 0 ) {
630 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->termmeta WHERE term_id = %d AND meta_key = %s", $term_id, $meta->meta_key ) );
631 } else {
632 delete_term_meta( $term_id, $meta->meta_key );
633 }
634 }
635
636 $message = sprintf( __( '%s Orphaned Term Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
637 }
638 break;
639 case 'orphan_term_relationships':
640 $query = $wpdb->get_results( "SELECT tr.object_id, tr.term_taxonomy_id, tt.term_id, tt.taxonomy FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy != 'link_category' AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts)" );
641 if( $query ) {
642 foreach ( $query as $tax ) {
643 $wp_remove_object_terms = wp_remove_object_terms( intval( $tax->object_id ), intval( $tax->term_id ), $tax->taxonomy );
644 if( $wp_remove_object_terms !== true ) {
645 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id = %d", $tax->object_id, $tax->term_taxonomy_id ) );
646 }
647 }
648
649 $message = sprintf( __( '%s Orphaned Term Relationships Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
650 }
651 break;
652 case 'unused_terms':
653 $query = $wpdb->get_results( $wpdb->prepare( "SELECT tt.term_taxonomy_id, t.term_id, tt.taxonomy FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.count = %d AND t.term_id NOT IN (" . implode( ',', $this->get_excluded_termids() ) . ")", 0 ) );
654 if( $query ) {
655 $check_wp_terms = false;
656 foreach ( $query as $tax ) {
657 if( taxonomy_exists( $tax->taxonomy ) ) {
658 wp_delete_term( intval( $tax->term_id ), $tax->taxonomy );
659 } else {
660 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", intval( $tax->term_taxonomy_id ) ) );
661 $check_wp_terms = true;
662 }
663 }
664 // We need this for invalid taxonomies
665 if( $check_wp_terms ) {
666 $wpdb->get_results( "DELETE FROM $wpdb->terms WHERE term_id NOT IN (SELECT term_id FROM $wpdb->term_taxonomy)" );
667 }
668
669 $message = sprintf( __( '%s Unused Terms Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
670 }
671 break;
672 case 'duplicated_postmeta':
673 $query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(meta_id ORDER BY meta_id DESC) AS ids, post_id, COUNT(*) AS count FROM $wpdb->postmeta GROUP BY post_id, meta_key, meta_value HAVING count > %d", 1 ) );
674 if( $query ) {
675 foreach ( $query as $meta ) {
676 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
677 array_pop( $ids );
678 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE meta_id IN (" . implode( ',', $ids ) . ") AND post_id = %d", intval( $meta->post_id ) ) );
679 }
680
681 $message = sprintf( __( '%s Duplicated Post Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
682 }
683 break;
684 case 'duplicated_commentmeta':
685 $query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(meta_id ORDER BY meta_id DESC) AS ids, comment_id, COUNT(*) AS count FROM $wpdb->commentmeta GROUP BY comment_id, meta_key, meta_value HAVING count > %d", 1 ) );
686 if( $query ) {
687 foreach ( $query as $meta ) {
688 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
689 array_pop( $ids );
690 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->commentmeta WHERE meta_id IN (" . implode( ',', $ids ) . ") AND comment_id = %d", intval( $meta->comment_id ) ) );
691 }
692
693 $message = sprintf( __( '%s Duplicated Comment Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
694 }
695 break;
696 case 'duplicated_usermeta':
697 $query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(umeta_id ORDER BY umeta_id DESC) AS ids, user_id, COUNT(*) AS count FROM $wpdb->usermeta GROUP BY user_id, meta_key, meta_value HAVING count > %d", 1 ) );
698 if( $query ) {
699 foreach ( $query as $meta ) {
700 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
701 array_pop( $ids );
702 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE umeta_id IN (" . implode( ',', $ids ) . ") AND user_id = %d", intval( $meta->user_id ) ) );
703 }
704
705 $message = sprintf( __( '%s Duplicated User Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
706 }
707 break;
708 case 'duplicated_termmeta':
709 $query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(meta_id ORDER BY meta_id DESC) AS ids, term_id, COUNT(*) AS count FROM $wpdb->termmeta GROUP BY term_id, meta_key, meta_value HAVING count > %d", 1 ) );
710 if( $query ) {
711 foreach ( $query as $meta ) {
712 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
713 array_pop( $ids );
714 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->termmeta WHERE meta_id IN (" . implode( ',', $ids ) . ") AND term_id = %d", intval( $meta->term_id ) ) );
715 }
716
717 $message = sprintf( __( '%s Duplicated Term Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
718 }
719 break;
720 case 'optimize_database':
721 $query = $wpdb->get_col( 'SHOW TABLES' );
722 if( $query ) {
723 $tables = implode( ',', $query );
724 $wpdb->query( "OPTIMIZE TABLE $tables" );
725 $message = sprintf( __( '%s Tables Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
726 }
727 break;
728 case 'oembed_postmeta':
729 $query = $wpdb->get_results( $wpdb->prepare( "SELECT post_id, meta_key FROM $wpdb->postmeta WHERE meta_key LIKE(%s)", '%_oembed_%' ) );
730 if( $query ) {
731 foreach ( $query as $meta ) {
732 $post_id = intval( $meta->post_id );
733 if( $post_id === 0 ) {
734 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta->meta_key ) );
735 } else {
736 delete_post_meta( $post_id, $meta->meta_key );
737 }
738 }
739
740 $message = sprintf( __( '%s oEmbed Caches In Post Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
741 }
742 break;
743 }
744
745 return apply_filters( 'wp_sweep_sweep', $message, $name );
746 }
747
748 /**
749 * Format number to percentage, taking care of division by 0.
750 * Props @barisunver https://github.com/barisunver
751 *
752 * @since 1.0.2
753 *
754 * @access public
755 * @param int $current
756 * @param int $total
757 * @return string Number in percentage
758 */
759 public function format_percentage( $current, $total ) {
760 return ( $total > 0 ? round( ( $current / $total ) * 100, 2 ) : 0 ) . '%';
761 }
762
763 /*
764 * Get excluded term IDs
765 *
766 * @since 1.0.3
767 *
768 * @access private
769 * @return array Excluded term IDs
770 */
771 private function get_excluded_termids() {
772 $default_term_ids = $this->get_default_taxonomy_termids();
773 if( ! is_array( $default_term_ids ) ) {
774 $default_term_ids = array();
775 }
776 $parent_term_ids = $this->get_parent_termids();
777 if( ! is_array( $parent_term_ids ) ) {
778 $parent_term_ids = array();
779 }
780 return array_merge( $default_term_ids, $parent_term_ids );
781 }
782
783 /*
784 * Get all default taxonomy term IDs
785 *
786 * @since 1.0.3
787 *
788 * @access private
789 * @return array Default taxonomy term IDs
790 */
791 private function get_default_taxonomy_termids() {
792 $taxonomies = get_taxonomies();
793 $default_term_ids = array();
794 if( $taxonomies ) {
795 $tax = array_keys( $taxonomies );
796 if( $tax ) {
797 foreach( $tax as $t ) {
798 $term_id = intval( get_option( 'default_' . $t ) );
799 if( $term_id > 0 ) {
800 $default_term_ids[] = $term_id;
801 }
802 }
803 }
804 }
805 return $default_term_ids;
806 }
807
808 /*
809 * Get terms that has a parent term
810 *
811 * @since 1.0.3
812 *
813 * @access private
814 * @return array Parent term IDs
815 */
816 private function get_parent_termids() {
817 global $wpdb;
818 return $wpdb->get_col( $wpdb->prepare( "SELECT tt.parent FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.parent > %d", 0 ) );
819 }
820
821 /**
822 * What to do when the plugin is being deactivated
823 *
824 * @since 1.0.0
825 *
826 * @access public
827 * @param boolean $network_wide
828 * @return void
829 */
830 public function plugin_activation( $network_wide ) {
831 if ( is_multisite() && $network_wide ) {
832 $ms_sites = wp_get_sites();
833
834 if( 0 < sizeof( $ms_sites ) ) {
835 foreach ( $ms_sites as $ms_site ) {
836 switch_to_blog( $ms_site['blog_id'] );
837 $this->plugin_activated();
838 restore_current_blog();
839 }
840 }
841 } else {
842 $this->plugin_activated();
843 }
844 }
845
846 /**
847 * Perform plugin activation tasks
848 *
849 * @since 1.0.0
850 *
851 * @access private
852 * @return void
853 */
854 private function plugin_activated() {}
855
856 /**
857 * What to do when the plugin is being activated
858 *
859 * @since 1.0.0
860 *
861 * @access public
862 * @param boolean $network_wide
863 * @return void
864 */
865 public function plugin_deactivation( $network_wide ) {
866 if ( is_multisite() && $network_wide ) {
867 $ms_sites = wp_get_sites();
868
869 if( 0 < sizeof( $ms_sites ) ) {
870 foreach ( $ms_sites as $ms_site ) {
871 switch_to_blog( $ms_site['blog_id'] );
872 $this->plugin_deactivated();
873 restore_current_blog();
874 }
875 }
876 } else {
877 $this->plugin_deactivated();
878 }
879 }
880
881 /**
882 * Perform plugin deactivation tasks
883 *
884 * @since 1.0.0
885 *
886 * @access private
887 * @return void
888 */
889 private function plugin_deactivated() {}
890 }
891
892 /**
893 * Init WP-Sweep
894 */
895 WPSweep::get_instance();
896