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