PluginProbe ʕ •ᴥ•ʔ
WP-Sweep / 1.0.8
WP-Sweep v1.0.8
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 class-command.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
916 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.8
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.8' );
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 // include class for WP CLI command
106 if ( defined( 'WP_CLI' ) ) {
107 require __DIR__ . '/class-command.php';
108 }
109 }
110
111 /**
112 * Adds all the plugin hooks
113 *
114 * @since 1.0.0
115 *
116 * @access public
117 * @return void
118 */
119 public function add_hooks() {
120 // Actions
121 add_action( 'init', array( $this, 'init' ) );
122 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
123 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
124 add_action( 'wp_ajax_sweep_details', array( $this, 'ajax_sweep_details' ) );
125 add_action( 'wp_ajax_sweep', array( $this, 'ajax_sweep' ) );
126 }
127
128 /**
129 * Enqueue JS/CSS files used for admin
130 *
131 * @since 1.0.3
132 *
133 * @access public
134 * @param string $hook
135 * @return void
136 */
137 public function admin_enqueue_scripts( $hook ) {
138 if( 'wp-sweep/admin.php' !== $hook ) {
139 return;
140 }
141
142 if( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
143 wp_enqueue_script( 'wp-sweep', plugins_url( 'wp-sweep/js/wp-sweep.js' ), array( 'jquery' ) , WP_SWEEP_VERSION, true );
144 } else {
145 wp_enqueue_script( 'wp-sweep', plugins_url( 'wp-sweep/js/wp-sweep.min.js' ), array( 'jquery' ) , WP_SWEEP_VERSION, true );
146 }
147
148 wp_localize_script( 'wp-sweep', 'wp_sweep', array(
149 'text_close_warning' => __( 'Sweeping is in progress. If you leave now, the process won\'t be completed.', 'wp-sweep' ),
150 'text_sweep' => __( 'Sweep', 'wp-sweep' ),
151 'text_sweep_all' => __( 'Sweep All', 'wp-sweep' ),
152 'text_sweeping' => __( 'Sweeping ...', 'wp-sweep' ),
153 'text_na' => __( 'N/A', 'wp-sweep' )
154 ) );
155 }
156
157 /**
158 * Admin menu
159 *
160 * @since 1.0.3
161 *
162 * @access public
163 * @return void
164 */
165 public function admin_menu() {
166 add_management_page( __( 'Sweep', 'wp-sweep' ), __( 'Sweep', 'wp-sweep' ), 'activate_plugins', 'wp-sweep/admin.php' );
167 }
168
169
170 /**
171 * Sweep Details loaded via AJAX
172 *
173 * @since 1.0.3
174 *
175 * @access public
176 * @return void
177 */
178 public function ajax_sweep_details() {
179 if( ! empty( $_GET['action'] ) && $_GET['action'] === 'sweep_details' && ! empty( $_GET['sweep_name'] ) && ! empty( $_GET['sweep_type'] ) ) {
180 // Verify Referer
181 if ( ! check_admin_referer( 'wp_sweep_details_' . $_GET['sweep_name'] ) ) {
182 wp_send_json_error( array(
183 'error' => __( 'Failed to verify referrer.', 'wp-sweep' )
184 ) );
185 } else {
186 wp_send_json_success( $this->details( $_GET['sweep_name'] ) );
187 }
188 }
189 }
190
191 /**
192 * Sweep via AJAX
193 *
194 * @since 1.0.3
195 *
196 * @access public
197 * @return void
198 */
199 public function ajax_sweep() {
200 if( ! empty( $_GET['action'] ) && $_GET['action'] === 'sweep' && ! empty( $_GET['sweep_name'] ) && ! empty( $_GET['sweep_type'] ) ) {
201 // Verify Referer
202 if ( ! check_admin_referer( 'wp_sweep_' . $_GET['sweep_name'] ) ) {
203 wp_send_json_error( array(
204 'error' => __( 'Failed to verify referrer.', 'wp-sweep' )
205 ) );
206 } else {
207 $sweep = $this->sweep( $_GET['sweep_name'] );
208 $count = $this->count( $_GET['sweep_name'] );
209 $total_count = $this->total_count( $_GET['sweep_type'] );
210 $total_stats = array();
211 switch( $_GET['sweep_type'] ) {
212 case 'posts':
213 case 'postmeta':
214 $total_stats = array( 'posts' => $this->total_count( 'posts' ), 'postmeta' => $this->total_count( 'postmeta' ) );
215 break;
216 case 'comments':
217 case 'commentmeta':
218 $total_stats = array( 'comments' => $this->total_count( 'comments' ), 'commentmeta' => $this->total_count( 'commentmeta' ) );
219 break;
220 case 'users':
221 case 'usermeta':
222 $total_stats = array( 'users' => $this->total_count( 'users' ), 'usermeta' => $this->total_count( 'usermeta' ) );
223 break;
224 case 'term_relationships':
225 case 'term_taxonomy':
226 case 'terms':
227 case 'termmeta':
228 $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' ) );
229 break;
230 case 'options':
231 $total_stats = array( 'options' => $this->total_count( 'options' ) );
232 break;
233 case 'tables':
234 $total_stats = array( 'tables' => $this->total_count( 'tables' ) );
235 break;
236 }
237
238 wp_send_json_success( array(
239 'sweep' => $sweep,
240 'count' => $count,
241 'total' => $total_count,
242 'percentage' => $this->format_percentage( $count, $total_count ),
243 'stats' => $total_stats
244 ) );
245 }
246 }
247 }
248
249 /**
250 * Count the number of total items belonging to each sweep
251 *
252 * @since 1.0.0
253 *
254 * @access public
255 * @param string $name
256 * @return int Number of items belonging to each sweep
257 */
258 public function total_count( $name ) {
259 global $wpdb;
260
261 $count = 0;
262
263 switch( $name ) {
264 case 'posts':
265 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts" );
266 break;
267 case 'postmeta':
268 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->postmeta" );
269 break;
270 case 'comments':
271 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments" );
272 break;
273 case 'commentmeta':
274 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->commentmeta" );
275 break;
276 case 'users':
277 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users" );
278 break;
279 case 'usermeta':
280 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta" );
281 break;
282 case 'term_relationships':
283 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
284 break;
285 case 'term_taxonomy':
286 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
287 break;
288 case 'terms':
289 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
290 break;
291 case 'termmeta':
292 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->termmeta" );
293 break;
294 case 'options':
295 $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->options" );
296 break;
297 case 'tables':
298 $count = sizeof( $wpdb->get_col( 'SHOW TABLES' ) );
299 break;
300 }
301
302 return apply_filters( 'wp_sweep_total_count', $count, $name );
303 }
304
305 /**
306 * Count the number of items belonging to each sweep
307 *
308 * @since 1.0.0
309 *
310 * @access public
311 * @param string $name
312 * @return int Number of items belonging to each sweep
313 */
314 public function count( $name ) {
315 global $wpdb;
316
317 $count = 0;
318
319 switch( $name ) {
320 case 'revisions':
321 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = %s", 'revision' ) );
322 break;
323 case 'auto_drafts':
324 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = %s", 'auto-draft' ) );
325 break;
326 case 'deleted_posts':
327 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = %s", 'trash' ) );
328 break;
329 case 'unapproved_comments':
330 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = %s", '0' ) );
331 break;
332 case 'spam_comments':
333 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = %s", 'spam' ) );
334 break;
335 case 'deleted_comments':
336 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s)", 'trash', 'post-trashed' ) );
337 break;
338 case 'transient_options':
339 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(option_id) FROM $wpdb->options WHERE option_name LIKE(%s)", '%_transient_%' ) );
340 break;
341 case 'orphan_postmeta':
342 $count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts)" );
343 break;
344 case 'orphan_commentmeta':
345 $count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)" );
346 break;
347 case 'orphan_usermeta':
348 $count = $wpdb->get_var( "SELECT COUNT(umeta_id) FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)" );
349 break;
350 case 'orphan_termmeta':
351 $count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)" );
352 break;
353 case 'orphan_term_relationships':
354 $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 NOT IN ('" . implode( '\',\'', $this->get_excluded_taxonomies() ) . "') AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts)" );
355 break;
356 case 'unused_terms':
357 $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 ) );
358 break;
359 case 'duplicated_postmeta':
360 $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 ) );
361 if( is_array( $query ) ) {
362 $count = array_sum( array_map( 'intval', $query ) );
363 }
364 break;
365 case 'duplicated_commentmeta':
366 $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 ) );
367 if( is_array( $query ) ) {
368 $count = array_sum( array_map( 'intval', $query ) );
369 }
370 break;
371 case 'duplicated_usermeta':
372 $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 ) );
373 if( is_array( $query ) ) {
374 $count = array_sum( array_map( 'intval', $query ) );
375 }
376 break;
377 case 'duplicated_termmeta':
378 $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 ) );
379 if( is_array( $query ) ) {
380 $count = array_sum( array_map( 'intval', $query ) );
381 }
382 break;
383 case 'optimize_database':
384 $count = sizeof( $wpdb->get_col( 'SHOW TABLES' ) );
385 break;
386 case 'oembed_postmeta':
387 $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(meta_id) FROM $wpdb->postmeta WHERE meta_key LIKE(%s)", '%_oembed_%' ) );
388 break;
389 }
390
391 return apply_filters( 'wp_sweep_count', $count, $name );
392 }
393
394 /**
395 * Return more details about a sweep
396 *
397 * @since 1.0.3
398 *
399 * @access public
400 * @param string $name
401 * @return int Number of items belonging to each sweep
402 */
403 public function details( $name ) {
404 global $wpdb;
405
406 $details = array();
407
408 switch( $name ) {
409 case 'revisions':
410 $details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_type = %s LIMIT %d", 'revision', $this->limit_details ) );
411 break;
412 case 'auto_drafts':
413 $details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_status = %s LIMIT %d", 'auto-draft', $this->limit_details ) );
414 break;
415 case 'deleted_posts':
416 $details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_status = %s LIMIT %d", 'trash', $this->limit_details ) );
417 break;
418 case 'unapproved_comments':
419 $details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE comment_approved = %s LIMIT %d", '0', $this->limit_details ) );
420 break;
421 case 'spam_comments':
422 $details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE comment_approved = %s LIMIT %d", 'spam', $this->limit_details ) );
423 break;
424 case 'deleted_comments':
425 $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 ) );
426 break;
427 case 'transient_options':
428 $details = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE(%s) LIMIT %d", '%_transient_%', $this->limit_details ) );
429 break;
430 case 'orphan_postmeta':
431 $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 ) );
432 break;
433 case 'orphan_commentmeta':
434 $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 ) );
435 break;
436 case 'orphan_usermeta':
437 $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 ) );
438 break;
439 case 'orphan_termmeta':
440 $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 ) );
441 break;
442 case 'orphan_term_relationships':
443 $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 NOT IN ('" . implode( '\',\'', $this->get_excluded_taxonomies() ) . "') AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts) LIMIT %d", $this->limit_details ) );
444 break;
445 case 'unused_terms':
446 $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 ) );
447 break;
448 case 'duplicated_postmeta':
449 $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 ) );
450 $details = array();
451 if( $query ) {
452 foreach( $query as $meta ) {
453 $details[] = $meta->meta_key;
454 }
455 }
456 break;
457 case 'duplicated_commentmeta':
458 $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 ) );
459 $details = array();
460 if( $query ) {
461 foreach( $query as $meta ) {
462 $details[] = $meta->meta_key;
463 }
464 }
465 break;
466 case 'duplicated_usermeta':
467 $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 ) );
468 $details = array();
469 if( $query ) {
470 foreach( $query as $meta ) {
471 $details[] = $meta->meta_key;
472 }
473 }
474 break;
475 case 'duplicated_termmeta':
476 $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 ) );
477 $details = array();
478 if( $query ) {
479 foreach( $query as $meta ) {
480 $details[] = $meta->meta_key;
481 }
482 }
483 break;
484 case 'optimize_database':
485 $details = $wpdb->get_col( 'SHOW TABLES' );
486 break;
487 case 'oembed_postmeta':
488 $details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->postmeta WHERE meta_key LIKE(%s) LIMIT %d", '%_oembed_%', $this->limit_details ) );
489 break;
490 }
491
492 return apply_filters( 'wp_sweep_details', $details, $name );
493 }
494
495 /**
496 * Does the sweeping/cleaning up
497 *
498 * @since 1.0.0
499 *
500 * @access public
501 * @param string $name
502 * @return string Processed message
503 */
504 public function sweep( $name ) {
505 global $wpdb;
506
507 $message = '';
508
509 switch( $name ) {
510 case 'revisions':
511 $query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = %s", 'revision' ) );
512 if( $query ) {
513 foreach ( $query as $id ) {
514 wp_delete_post_revision( intval( $id ) );
515 }
516
517 $message = sprintf( __( '%s Revisions Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
518 }
519 break;
520 case 'auto_drafts':
521 $query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_status = %s", 'auto-draft' ) );
522 if( $query ) {
523 foreach ( $query as $id ) {
524 wp_delete_post( intval( $id ), true );
525 }
526
527 $message = sprintf( __( '%s Auto Drafts Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
528 }
529 break;
530 case 'deleted_posts':
531 $query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_status = %s", 'trash' ) );
532 if( $query ) {
533 foreach ( $query as $id ) {
534 wp_delete_post( $id, true );
535 }
536
537 $message = sprintf( __( '%s Deleted Posts Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
538 }
539 break;
540 case 'unapproved_comments':
541 $query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_approved = %s", '0' ) );
542 if( $query ) {
543 foreach ( $query as $id ) {
544 wp_delete_comment( intval( $id ), true );
545 }
546
547 $message = sprintf( __( '%s Unapproved Comments Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
548 }
549 break;
550 case 'spam_comments':
551 $query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_approved = %s", 'spam' ) );
552 if( $query ) {
553 foreach ( $query as $id ) {
554 wp_delete_comment( intval( $id ), true );
555 }
556
557 $message = sprintf( __( '%s Spam Comments Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
558 }
559 break;
560 case 'deleted_comments':
561 $query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s)", 'trash', 'post-trashed' ) );
562 if( $query ) {
563 foreach ( $query as $id ) {
564 wp_delete_comment( intval( $id ), true );
565 }
566
567 $message = sprintf( __( '%s Trash Comments Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
568 }
569 break;
570 case 'transient_options':
571 $query = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE(%s)", '%_transient_%' ) );
572 if( $query ) {
573 foreach ( $query as $option_name ) {
574 if( strpos( $option_name, '_site_transient_' ) !== false ) {
575 delete_site_transient( str_replace( '_site_transient_', '', $option_name ) );
576 } else {
577 delete_transient( str_replace( '_transient_', '', $option_name ) );
578 }
579 }
580
581 $message = sprintf( __( '%s Transient Options Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
582 }
583 break;
584 case 'orphan_postmeta':
585 $query = $wpdb->get_results( "SELECT post_id, meta_key FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts)" );
586 if( $query ) {
587 foreach ( $query as $meta ) {
588 $post_id = intval( $meta->post_id );
589 if( $post_id === 0 ) {
590 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta->meta_key ) );
591 } else {
592 delete_post_meta( $post_id, $meta->meta_key );
593 }
594 }
595
596 $message = sprintf( __( '%s Orphaned Post Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
597 }
598 break;
599 case 'orphan_commentmeta':
600 $query = $wpdb->get_results( "SELECT comment_id, meta_key FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)" );
601 if( $query ) {
602 foreach ( $query as $meta ) {
603 $comment_id = intval( $meta->comment_id );
604 if( $comment_id === 0 ) {
605 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->commentmeta WHERE comment_id = %d AND meta_key = %s", $comment_id, $meta->meta_key ) );
606 } else {
607 delete_comment_meta( $comment_id, $meta->meta_key );
608 }
609 }
610
611 $message = sprintf( __( '%s Orphaned Comment Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
612 }
613 break;
614 case 'orphan_usermeta':
615 $query = $wpdb->get_results( "SELECT user_id, meta_key FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)" );
616 if( $query ) {
617 foreach ( $query as $meta ) {
618 $user_id = intval( $meta->user_id );
619 if( $user_id === 0 ) {
620 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $user_id, $meta->meta_key ) );
621 } else {
622 delete_user_meta( $user_id, $meta->meta_key );
623 }
624 }
625
626 $message = sprintf( __( '%s Orphaned User Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
627 }
628 break;
629 case 'orphan_termmeta':
630 $query = $wpdb->get_results( "SELECT term_id, meta_key FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)" );
631 if( $query ) {
632 foreach ( $query as $meta ) {
633 $term_id = intval( $meta->term_id );
634 if( $term_id === 0 ) {
635 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->termmeta WHERE term_id = %d AND meta_key = %s", $term_id, $meta->meta_key ) );
636 } else {
637 delete_term_meta( $term_id, $meta->meta_key );
638 }
639 }
640
641 $message = sprintf( __( '%s Orphaned Term Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
642 }
643 break;
644 case 'orphan_term_relationships':
645 $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 NOT IN ('" . implode( '\',\'', $this->get_excluded_taxonomies() ) . "') AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts)" );
646 if( $query ) {
647 foreach ( $query as $tax ) {
648 $wp_remove_object_terms = wp_remove_object_terms( intval( $tax->object_id ), intval( $tax->term_id ), $tax->taxonomy );
649 if( $wp_remove_object_terms !== true ) {
650 $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 ) );
651 }
652 }
653
654 $message = sprintf( __( '%s Orphaned Term Relationships Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
655 }
656 break;
657 case 'unused_terms':
658 $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 ) );
659 if( $query ) {
660 $check_wp_terms = false;
661 foreach ( $query as $tax ) {
662 if( taxonomy_exists( $tax->taxonomy ) ) {
663 wp_delete_term( intval( $tax->term_id ), $tax->taxonomy );
664 } else {
665 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", intval( $tax->term_taxonomy_id ) ) );
666 $check_wp_terms = true;
667 }
668 }
669 // We need this for invalid taxonomies
670 if( $check_wp_terms ) {
671 $wpdb->get_results( "DELETE FROM $wpdb->terms WHERE term_id NOT IN (SELECT term_id FROM $wpdb->term_taxonomy)" );
672 }
673
674 $message = sprintf( __( '%s Unused Terms Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
675 }
676 break;
677 case 'duplicated_postmeta':
678 $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 ) );
679 if( $query ) {
680 foreach ( $query as $meta ) {
681 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
682 array_pop( $ids );
683 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE meta_id IN (" . implode( ',', $ids ) . ") AND post_id = %d", intval( $meta->post_id ) ) );
684 }
685
686 $message = sprintf( __( '%s Duplicated Post Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
687 }
688 break;
689 case 'duplicated_commentmeta':
690 $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 ) );
691 if( $query ) {
692 foreach ( $query as $meta ) {
693 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
694 array_pop( $ids );
695 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->commentmeta WHERE meta_id IN (" . implode( ',', $ids ) . ") AND comment_id = %d", intval( $meta->comment_id ) ) );
696 }
697
698 $message = sprintf( __( '%s Duplicated Comment Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
699 }
700 break;
701 case 'duplicated_usermeta':
702 $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 ) );
703 if( $query ) {
704 foreach ( $query as $meta ) {
705 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
706 array_pop( $ids );
707 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE umeta_id IN (" . implode( ',', $ids ) . ") AND user_id = %d", intval( $meta->user_id ) ) );
708 }
709
710 $message = sprintf( __( '%s Duplicated User Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
711 }
712 break;
713 case 'duplicated_termmeta':
714 $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 ) );
715 if( $query ) {
716 foreach ( $query as $meta ) {
717 $ids = array_map( 'intval', explode( ',', $meta->ids ) );
718 array_pop( $ids );
719 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->termmeta WHERE meta_id IN (" . implode( ',', $ids ) . ") AND term_id = %d", intval( $meta->term_id ) ) );
720 }
721
722 $message = sprintf( __( '%s Duplicated Term Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
723 }
724 break;
725 case 'optimize_database':
726 $query = $wpdb->get_col( 'SHOW TABLES' );
727 if( $query ) {
728 $tables = implode( ',', $query );
729 $wpdb->query( "OPTIMIZE TABLE $tables" );
730 $message = sprintf( __( '%s Tables Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
731 }
732 break;
733 case 'oembed_postmeta':
734 $query = $wpdb->get_results( $wpdb->prepare( "SELECT post_id, meta_key FROM $wpdb->postmeta WHERE meta_key LIKE(%s)", '%_oembed_%' ) );
735 if( $query ) {
736 foreach ( $query as $meta ) {
737 $post_id = intval( $meta->post_id );
738 if( $post_id === 0 ) {
739 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta->meta_key ) );
740 } else {
741 delete_post_meta( $post_id, $meta->meta_key );
742 }
743 }
744
745 $message = sprintf( __( '%s oEmbed Caches In Post Meta Processed', 'wp-sweep' ), number_format_i18n( sizeof( $query ) ) );
746 }
747 break;
748 }
749
750 return apply_filters( 'wp_sweep_sweep', $message, $name );
751 }
752
753 /**
754 * Format number to percentage, taking care of division by 0.
755 * Props @barisunver https://github.com/barisunver
756 *
757 * @since 1.0.2
758 *
759 * @access public
760 * @param int $current
761 * @param int $total
762 * @return string Number in percentage
763 */
764 public function format_percentage( $current, $total ) {
765 return ( $total > 0 ? round( ( $current / $total ) * 100, 2 ) : 0 ) . '%';
766 }
767
768 /*
769 * Get excluded taxonomies
770 *
771 * @since 1.0.8
772 *
773 * @access private
774 * @return array Excluded taxonomies
775 */
776 private function get_excluded_taxonomies() {
777 $excluded_taxonomies = array();
778 $excluded_taxonomies[] = 'link_category';
779
780 return apply_filters( 'wp_sweep_excluded_taxonomies', $excluded_taxonomies );
781 }
782
783 /*
784 * Get excluded term IDs
785 *
786 * @since 1.0.3
787 *
788 * @access private
789 * @return array Excluded term IDs
790 */
791 private function get_excluded_termids() {
792 $default_term_ids = $this->get_default_taxonomy_termids();
793 if( ! is_array( $default_term_ids ) ) {
794 $default_term_ids = array();
795 }
796 $parent_term_ids = $this->get_parent_termids();
797 if( ! is_array( $parent_term_ids ) ) {
798 $parent_term_ids = array();
799 }
800 return array_merge( $default_term_ids, $parent_term_ids );
801 }
802
803 /*
804 * Get all default taxonomy term IDs
805 *
806 * @since 1.0.3
807 *
808 * @access private
809 * @return array Default taxonomy term IDs
810 */
811 private function get_default_taxonomy_termids() {
812 $taxonomies = get_taxonomies();
813 $default_term_ids = array();
814 if( $taxonomies ) {
815 $tax = array_keys( $taxonomies );
816 if( $tax ) {
817 foreach( $tax as $t ) {
818 $term_id = intval( get_option( 'default_' . $t ) );
819 if( $term_id > 0 ) {
820 $default_term_ids[] = $term_id;
821 }
822 }
823 }
824 }
825 return $default_term_ids;
826 }
827
828 /*
829 * Get terms that has a parent term
830 *
831 * @since 1.0.3
832 *
833 * @access private
834 * @return array Parent term IDs
835 */
836 private function get_parent_termids() {
837 global $wpdb;
838 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 ) );
839 }
840
841 /**
842 * What to do when the plugin is being deactivated
843 *
844 * @since 1.0.0
845 *
846 * @access public
847 * @param boolean $network_wide
848 * @return void
849 */
850 public function plugin_activation( $network_wide ) {
851 if ( is_multisite() && $network_wide ) {
852 $ms_sites = wp_get_sites();
853
854 if( 0 < sizeof( $ms_sites ) ) {
855 foreach ( $ms_sites as $ms_site ) {
856 switch_to_blog( $ms_site['blog_id'] );
857 $this->plugin_activated();
858 restore_current_blog();
859 }
860 }
861 } else {
862 $this->plugin_activated();
863 }
864 }
865
866 /**
867 * Perform plugin activation tasks
868 *
869 * @since 1.0.0
870 *
871 * @access private
872 * @return void
873 */
874 private function plugin_activated() {}
875
876 /**
877 * What to do when the plugin is being activated
878 *
879 * @since 1.0.0
880 *
881 * @access public
882 * @param boolean $network_wide
883 * @return void
884 */
885 public function plugin_deactivation( $network_wide ) {
886 if ( is_multisite() && $network_wide ) {
887 $ms_sites = wp_get_sites();
888
889 if( 0 < sizeof( $ms_sites ) ) {
890 foreach ( $ms_sites as $ms_site ) {
891 switch_to_blog( $ms_site['blog_id'] );
892 $this->plugin_deactivated();
893 restore_current_blog();
894 }
895 }
896 } else {
897 $this->plugin_deactivated();
898 }
899 }
900
901 /**
902 * Perform plugin deactivation tasks
903 *
904 * @since 1.0.0
905 *
906 * @access private
907 * @return void
908 */
909 private function plugin_deactivated() {}
910 }
911
912 /**
913 * Init WP-Sweep
914 */
915 WPSweep::get_instance();
916