PluginProbe ʕ •ᴥ•ʔ
Media Cleaner: Clean your WordPress! / 4.6.3
Media Cleaner: Clean your WordPress! v4.6.3
7.1.1 7.1.0 7.0.9 7.0.8 trunk 3.6.8 3.6.9 3.7.0 3.8.0 3.9.0 4.0.0 4.0.2 4.0.4 4.0.6 4.0.7 4.1.0 4.2.0 4.2.2 4.2.3 4.2.4 4.2.5 4.4.0 4.4.2 4.4.4 4.4.6 4.4.7 4.4.8 4.5.0 4.5.4 4.5.6 4.5.7 4.5.8 4.6.2 4.6.3 4.8.0 4.8.4 5.0.0 5.0.1 5.1.0 5.1.1 5.1.3 5.2.0 5.2.1 5.2.4 5.4.0 5.4.1 5.4.2 5.4.3 5.4.4 5.4.5 5.4.6 5.4.9 5.5.0 5.5.1 5.5.2 5.5.3 5.5.4 5.5.7 5.5.8 5.6.1 5.6.2 5.6.3 5.6.4 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.0.6 6.0.7 6.0.8 6.0.9 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.1.9 6.2.0 6.2.1 6.2.3 6.2.4 6.2.5 6.2.6 6.2.7 6.2.8 6.3.0 6.3.1 6.3.2 6.3.4 6.3.5 6.3.7 6.3.8 6.3.9 6.4.0 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.4.7 6.4.8 6.4.9 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.5.8 6.5.9 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.6.6 6.6.7 6.6.8 6.6.9 6.7.0 6.7.1 6.7.2 6.7.3 6.7.4 6.7.5 6.7.6 6.7.7 6.7.8 6.7.9 6.8.0 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.8.6 6.8.7 6.8.8 6.8.9 6.9.0 6.9.1 6.9.2 6.9.3 6.9.4 6.9.5 6.9.6 6.9.7 6.9.8 6.9.9 7.0.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.0.6 7.0.7
media-cleaner / core.php
media-cleaner Last commit date
common 8 years ago admin.php 8 years ago checkers.php 8 years ago core.php 8 years ago custom_checkers.php 8 years ago media-cleaner.css 9 years ago media-cleaner.js 8 years ago media-cleaner.php 8 years ago readme.txt 8 years ago scan.php 8 years ago wpmc_admin.php 8 years ago wpmc_checkers.php 8 years ago wpmc_custom_checkers.php 9 years ago wpmc_scan.php 8 years ago
core.php
1612 lines
1 <?php
2
3 class Meow_WPMC_Core {
4
5 public $checkers = null;
6 public $admin = null;
7 public $last_analysis = null;
8 public $last_analysis_ids = null;
9 private $regex_file = '/[A-Za-z0-9-_,\s]+[.]{1}(MIMETYPES)/';
10 public $current_method = 'media';
11 private $refcache = array();
12
13 public function __construct( $admin ) {
14 $this->admin = $admin;
15 add_action( 'admin_init', array( $this, 'admin_init' ) );
16 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
17 add_action( 'admin_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
18 add_action( 'admin_print_scripts', array( $this, 'admin_inline_js' ) );
19 add_action( 'wp_ajax_wpmc_scan', array( $this, 'wp_ajax_wpmc_scan' ) );
20 add_action( 'wp_ajax_wpmc_get_all_issues', array( $this, 'wp_ajax_wpmc_get_all_issues' ) );
21 add_action( 'wp_ajax_wpmc_get_all_deleted', array( $this, 'wp_ajax_wpmc_get_all_deleted' ) );
22 add_action( 'wp_ajax_wpmc_scan_do', array( $this, 'wp_ajax_wpmc_scan_do' ) );
23 add_action( 'wp_ajax_wpmc_prepare_do', array( $this, 'wp_ajax_wpmc_prepare_do' ) );
24 add_action( 'wp_ajax_wpmc_delete_do', array( $this, 'wp_ajax_wpmc_delete_do' ) );
25 add_action( 'wp_ajax_wpmc_ignore_do', array( $this, 'wp_ajax_wpmc_ignore_do' ) );
26 add_action( 'wp_ajax_wpmc_recover_do', array( $this, 'wp_ajax_wpmc_recover_do' ) );
27 add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 10, 2 );
28 add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
29 }
30
31 function admin_init() {
32 $this->current_method = get_option( 'wpmc_method', 'media' );
33 $types = "jpg|jpeg|jpe|gif|png|tiff|bmp|csv|pdf|xls|xlsx|doc|docx|tiff|mp3|mp4|wav|lua";
34 $this->regex_file = str_replace( "MIMETYPES", $types, $this->regex_file );
35 require( 'scan.php' );
36 require( 'checkers.php' );
37 new MeowApps_WPMC_Scan( $this );
38 $this->checkers = new Meow_WPMC_Checkers( $this );
39 }
40
41 /*******************************************************************************
42 * METABOX FOR USAGE
43 ******************************************************************************/
44
45 function add_metabox() {
46 add_meta_box( 'mfrh_media_usage_box', 'Media Cleaner', array( $this, 'display_metabox' ), 'attachment', 'side', 'default' );
47 }
48
49 function display_metabox( $post ) {
50 $this->log( "Media Edit > Checking Media #{$post->ID}" );
51 $success = $this->wpmc_check_media( $post->ID, true );
52 $this->log( "Success $success\n" );
53 if ( $success ) {
54 if ( $this->last_analysis == "CONTENT" ) {
55 echo "Found in content.";
56 }
57 else if ( $this->last_analysis == "CONTENT (ID)" ) {
58 echo "Found in content (as an ID).";
59 }
60 else if ( $this->last_analysis == "CONTENT (URL)" ) {
61 echo "Found in content (as an URL).";
62 }
63 else if ( $this->last_analysis == "THEME" ) {
64 echo "Found in theme.";
65 }
66 else if ( $this->last_analysis == "PAGE BUILDER" ) {
67 echo "Found in Page Builder.";
68 }
69 else if ( $this->last_analysis == "GALLERY" ) {
70 echo "Found in gallery.";
71 }
72 else if ( $this->last_analysis == "META" ) {
73 echo "Found in meta.";
74 }
75 else if ( $this->last_analysis == "META (ID)" ) {
76 echo "Found in meta (as an ID).";
77 }
78 else if ( $this->last_analysis == "META (URL)" ) {
79 echo "Found in meta (as an URL).";
80 }
81 else if ( $this->last_analysis == "META ACF (ID)" ) {
82 echo "Found in ACF meta (as an ID).";
83 }
84 else if ( $this->last_analysis == "META ACF (URL)" ) {
85 echo "Found in ACF meta (as an URL).";
86 }
87 else if ( $this->last_analysis == "WIDGET" ) {
88 echo "Found in widget.";
89 }
90 else {
91 echo "It seems to be used as: " . $this->last_analysis;
92 }
93 }
94 else {
95 echo "Doesn't seem to be used.";
96 }
97 }
98
99 /*******************************************************************************
100 * ASYNCHRONOUS AJAX FUNCTIONS
101 ******************************************************************************/
102
103 function wp_ajax_wpmc_delete_do () {
104 ob_start();
105 $data = $_POST['data'];
106 $success = 0;
107 foreach ( $data as $piece ) {
108 $success += ( $this->wpmc_delete( $piece ) ? 1 : 0 );
109 }
110 ob_end_clean();
111 echo json_encode(
112 array(
113 'success' => true,
114 'result' => array( 'data' => $data, 'success' => $success ),
115 'message' => __( "Status unknown.", 'media-cleaner' )
116 )
117 );
118 die();
119 }
120
121 function wp_ajax_wpmc_ignore_do () {
122 ob_start();
123 $data = $_POST['data'];
124 $success = 0;
125 foreach ( $data as $piece ) {
126 $success += ( $this->wpmc_ignore( $piece ) ? 1 : 0 );
127 }
128 ob_end_clean();
129 echo json_encode(
130 array(
131 'success' => true,
132 'result' => array( 'data' => $data, 'success' => $success ),
133 'message' => __( "Status unknown.", 'media-cleaner' )
134 )
135 );
136 die();
137 }
138
139 function wp_ajax_wpmc_recover_do () {
140 ob_start();
141 $data = $_POST['data'];
142 $success = 0;
143 foreach ( $data as $piece ) {
144 $success += ( $this->wpmc_recover( $piece ) ? 1 : 0 );
145 }
146 ob_end_clean();
147 echo json_encode(
148 array(
149 'success' => true,
150 'result' => array( 'data' => $data, 'success' => $success ),
151 'message' => __( "Status unknown.", 'media-cleaner' )
152 )
153 );
154 die();
155 }
156
157 function deepsleep( $seconds ) {
158 $start_time = time();
159 while( true ) {
160 if ( ( time() - $start_time ) > $seconds ) {
161 return false;
162 }
163 get_post( array( 'posts_per_page' => 50 ) );
164 }
165 }
166
167 private $start_time;
168 private $time_elapsed = 0;
169 private $item_scan_avg_time = 0;
170 private $wordpress_init_time = 0.5;
171 private $max_execution_time;
172 private $items_checked = 0;
173 private $items_count = 0;
174
175 function timeout_check_start( $count ) {
176 $this->start_time = time();
177 $this->items_count = $count;
178 $this->max_execution_time = ini_get( "max_execution_time" );
179 if ( empty( $this->max_execution_time ) )
180 $this->max_execution_time = 30;
181 }
182
183 function timeout_check() {
184 $this->time_elapsed = time() - $this->start_time;
185 $this->time_remaining = $this->max_execution_time - $this->wordpress_init_time - $this->time_elapsed;
186 if ( $this->time_remaining - $this->item_scan_avg_time < 0 ) {
187 error_log("Media Cleaner Timeout! Check the Media Cleaner logs for more info.");
188 $this->log( "Timeout! Some info for debug:" );
189 $this->log( "Elapsed time: $this->time_elapsed" );
190 $this->log( "WP init time: $this->wordpress_init_time" );
191 $this->log( "Remaining time: $this->time_remaining" );
192 $this->log( "Scan time per item: $this->item_scan_avg_time" );
193 $this->log( "PHP max_execution_time: $this->max_execution_time" );
194 header("HTTP/1.0 408 Request Timeout");
195 exit;
196 }
197 }
198
199 function timeout_check_additem() {
200 $this->items_checked++;
201 $this->time_elapsed = time() - $this->start_time;
202 $this->item_scan_avg_time = ceil( ( $this->time_elapsed / $this->items_checked ) * 10 ) / 10;
203 }
204
205 function wp_ajax_wpmc_prepare_do() {
206 $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
207 $limitsize = get_option( 'wpmc_posts_buffer', 5 );
208 if ( empty( $limit ) )
209 $this->wpmc_reset_issues();
210
211 $method = get_option( 'wpmc_method', 'media' );
212 $check_library = get_option(' wpmc_media_library', true );
213 $check_postmeta = get_option( 'wpmc_postmeta', false );
214 $check_posts = get_option( 'wpmc_posts', false );
215 $check_widgets = get_option( 'wpmc_widgets', false );
216 if ( $method == 'media' && !$check_posts && !$check_postmeta && !$check_widgets ) {
217 echo json_encode( array(
218 'results' => array(),
219 'success' => true,
220 'finished' => true,
221 'message' => __( "Posts, Meta and Widgets analysis are all off. Done.", 'media-cleaner' )
222 ) );
223 die();
224 }
225 if ( $method == 'files' && $check_library && !$check_posts && !$check_postmeta && !$check_widgets ) {
226 echo json_encode( array(
227 'results' => array(),
228 'success' => true,
229 'finished' => true,
230 'message' => __( "Posts, Meta and Widgets analysis are all off. Done.", 'media-cleaner' )
231 ) );
232 die();
233 }
234
235 global $wpdb;
236 // Maybe we could avoid to check more post_types.
237 // SELECT post_type, COUNT(*) FROM `wp_posts` GROUP BY post_type
238 $posts = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
239 WHERE p.post_status != 'inherit'
240 AND p.post_status != 'trash'
241 AND p.post_type != 'attachment'
242 AND p.post_type != 'shop_order'
243 AND p.post_type != 'shop_order_refund'
244 AND p.post_type != 'nav_menu_item'
245 AND p.post_type != 'revision'
246 AND p.post_type != 'auto-draft'
247 AND p.post_type != 'wphb_minify_group'
248 AND p.post_type != 'customize_changeset'
249 AND p.post_type != 'oembed_cache'
250 AND p.post_type NOT LIKE '%acf-%'
251 AND p.post_type NOT LIKE '%edd_%'
252 LIMIT %d, %d", $limit, $limitsize
253 )
254 );
255
256 $found = array();
257
258 // Only at the beginning
259 if ( empty( $limit ) ) {
260
261 $this->log( "Analyzing for references:" );
262
263 $theme_ids = array();
264 $theme_urls = array();
265 $this->get_images_from_themes( $theme_ids, $theme_urls );
266 $this->add_reference_id( $theme_ids, 'THEME' );
267 $this->add_reference_url( $theme_urls, 'THEME' );
268
269 // Only on Start: Analyze Widgets
270 if ( get_option( 'wpmc_widgets', false ) ) {
271 $widgets_ids = array();
272 $widgets_urls = array();
273 $this->get_images_from_widgets( $widgets_ids, $widgets_urls );
274 $this->add_reference_id( $widgets_ids, 'WIDGET' );
275 $this->add_reference_url( $widgets_urls, 'WIDGET' );
276 }
277
278 // Only on Start: Analyze WooCommerce Categories Images
279 if ( class_exists( 'WooCommerce' ) ) {
280 $metas = $wpdb->get_col( "SELECT meta_value
281 FROM $wpdb->termmeta
282 WHERE meta_key LIKE '%thumbnail_id%'"
283 );
284 if ( count( $metas ) > 0 ) {
285 $postmeta_images_ids = array();
286 foreach ( $metas as $meta )
287 if ( is_numeric( $meta ) && $meta > 0 )
288 array_push( $postmeta_images_ids, $meta );
289 $this->add_reference_id( $postmeta_images_ids, 'META (ID)' );
290 }
291 }
292 }
293
294 $this->timeout_check_start( count( $posts ) );
295
296 foreach ( $posts as $post ) {
297 $this->timeout_check();
298 // Run the scanners
299 if ( $check_postmeta )
300 do_action( 'wpmc_scan_postmeta', $post );
301 if ( $check_posts ) {
302 // Get HTML for this post
303 $html = get_post_field( 'post_content', $post );
304 // Scan on the raw TML content
305 do_action( 'wpmc_scan_post', $html, $post );
306 $html = do_shortcode( $html );
307 $html = wp_make_content_images_responsive( $html );
308 // Scan with shortcodes resolved and src-set
309 do_action( 'wpmc_scan_post', $html, $post );
310 }
311 $this->timeout_check_additem();
312 }
313 // Write the references cached by the scanners
314 $this->write_references();
315
316 $finished = count( $posts ) < $limitsize;
317 if ( $finished ) {
318 $found = array();
319 // Optimize DB (but that takes too long!)
320 //$table_name = $wpdb->prefix . "mclean_refs";
321 // $wpdb->query ("DELETE a FROM $table_name as a, $table_name as b
322 // WHERE (a.mediaId = b.mediaId OR a.mediaId IS NULL AND b.mediaId IS NULL)
323 // AND (a.mediaUrl = b.mediaUrl OR a.mediaUrl IS NULL AND b.mediaUrl IS NULL)
324 // AND (a.originType = b.originType OR a.originType IS NULL AND b.originType IS NULL)
325 // AND (a.origin = b.origin OR a.origin IS NULL AND b.origin IS NULL)
326 // AND a.ID < b.ID;" );
327 // $wpdb->query ("DELETE a FROM $table_name as a, $table_name as b WHERE a.mediaId = b.mediaId AND a.mediaId > 0 AND a.ID < b.ID;" );
328 // $wpdb->query ("DELETE a FROM $table_name as a, $table_name as b WHERE a.mediaUrl = b.mediaUrl AND LENGTH(a.mediaUrl) > 1 AND a.ID < b.ID;" );
329 }
330 if ( $finished && get_option( 'wpmc_debuglogs', false ) ) {
331 //$this->log( print_r( $found, true ) );
332 }
333 echo json_encode(
334 array(
335 'success' => true,
336 'finished' => $finished,
337 'limit' => $limit + $limitsize,
338 'message' => __( "Posts checked.", 'media-cleaner' ) )
339 );
340 die();
341 }
342
343 function wp_ajax_wpmc_scan_do () {
344 // For debug, to pretend there is a timeout
345 //$this->deepsleep(10);
346 //header("HTTP/1.0 408 Request Timeout");
347 //exit;
348
349 ob_start();
350 $type = $_POST['type'];
351 $data = $_POST['data'];
352 $this->timeout_check_start( count( $data ) );
353 $success = 0;
354 foreach ( $data as $piece ) {
355 $this->timeout_check();
356 if ( $type == 'file' ) {
357 $this->log( "Check File: {$piece}" );
358 $result = ( apply_filters( 'wpmc_check_file', true, $piece ) ? 1 : 0 );
359 $this->log( "Success " . $result . "\n" );
360 $success += $result;
361 }
362 else if ( $type == 'media' ) {
363 $this->log( "Checking Media #{$piece}" );
364 $result = ( $this->wpmc_check_media( $piece ) ? 1 : 0 );
365 $this->log( "Success " . $result . "\n" );
366 $success += $result;
367 }
368 $this->timeout_check_additem();
369 }
370 ob_end_clean();
371 echo json_encode(
372 array(
373 'success' => true,
374 'result' => array( 'type' => $type, 'data' => $data, 'success' => $success ),
375 'message' => __( "Items checked.", 'media-cleaner' )
376 )
377 );
378 die();
379 }
380
381 function wp_ajax_wpmc_get_all_deleted () {
382 global $wpdb;
383 $table_name = $wpdb->prefix . "mclean_scan";
384 $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 1" );
385 echo json_encode(
386 array(
387 'results' => array( 'ids' => $ids ),
388 'success' => true,
389 'message' => __( "List generated.", 'media-cleaner' )
390 )
391 );
392 die;
393 }
394
395 function wp_ajax_wpmc_get_all_issues () {
396 global $wpdb;
397 $isTrash = ( isset( $_POST['isTrash'] ) && $_POST['isTrash'] == 1 ) ? true : false;
398 $table_name = $wpdb->prefix . "mclean_scan";
399 if ( $isTrash )
400 $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 1" );
401 else
402 $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 0" );
403 echo json_encode(
404 array(
405 'results' => array( 'ids' => $ids ),
406 'success' => true,
407 'message' => __( "List generated.", 'media-cleaner' )
408 )
409 );
410 die;
411 }
412
413 function array_to_ids_or_urls( &$meta, &$ids, &$urls ) {
414 foreach ( $meta as $k => $m ) {
415 if ( is_numeric( $m ) ) {
416 // Probably a Media ID
417 if ( $m > 0 )
418 array_push( $ids, $m );
419 }
420 else if ( is_array( $m ) ) {
421 // If it's an array with a width, probably that the index is the Media ID
422 if ( isset( $m['width'] ) && is_numeric( $k ) ) {
423 if ( $k > 0 )
424 array_push( $ids, $k );
425 }
426 }
427 else if ( !empty( $m ) ) {
428 // If it's a string, maybe it's a file (with an extension)
429 if ( preg_match( $this->regex_file, $m ) )
430 array_push( $urls, $m );
431 }
432 }
433 }
434
435 function get_images_from_widgets( &$ids, &$urls ) {
436 global $wp_registered_widgets;
437 $syswidgets = $wp_registered_widgets;
438 $active_widgets = get_option( 'sidebars_widgets' );
439 foreach ( $active_widgets as $sidebar_name => $widgets ) {
440 if ( $sidebar_name != 'wp_inactive_widgets' && !empty( $widgets ) && is_array( $widgets ) ) {
441 $i = 0;
442 foreach ( $widgets as $key => $widget ) {
443 $widget_class = $syswidgets[$widget]['callback'][0]->option_name;
444 $instance_id = $syswidgets[$widget]['params'][0]['number'];
445 $widget_data = get_option( $widget_class );
446 // error_log( "INSTANCE $key ($instance_id)" );
447 // error_log( print_r( $widget_data, 1 ) );
448 if ( !empty( $widget_data[$instance_id]['text'] ) ) {
449 $html = do_shortcode( $widget_data[$instance_id]['text'] );
450 $urls = array_merge( $urls, $this->get_urls_from_html( $html ) );
451 }
452 if ( !empty( $widget_data[$instance_id]['attachment_id'] ) ) {
453 $id = $widget_data[$instance_id]['attachment_id'];
454 array_push( $ids, $id );
455 }
456 if ( !empty( $widget_data[$instance_id]['url'] ) ) {
457 $url = $this->wpmc_clean_url( $widget_data[$instance_id]['url'] );
458 array_push( $urls, $url );
459 }
460 if ( !empty( $widget_data[$instance_id]['ids'] ) ) {
461 $newIds = $widget_data[$instance_id]['ids'];
462 $ids = array_merge( $ids, $newIds );
463 }
464 $i++;
465 }
466 }
467 }
468 }
469
470 function get_urls_from_html( $html ) {
471 if ( empty( $html ) )
472 return array();
473 libxml_use_internal_errors( false );
474
475 $dom = new DOMDocument();
476 @$dom->loadHTML( $html );
477
478 // Images, src, srcset
479 $imgs = $dom->getElementsByTagName( 'img' );
480 $results = array();
481 foreach ( $imgs as $img ) {
482 $src = $this->wpmc_clean_url( $img->getAttribute('src') );
483 array_push( $results, $src );
484 $srcset = $img->getAttribute('srcset');
485 if ( !empty( $srcset ) ) {
486 $setImgs = explode( ',', trim( $srcset ) );
487 foreach ( $setImgs as $setImg ) {
488 $finalSetImg = explode( ' ', trim( $setImg ) );
489 if ( is_array( $finalSetImg ) ) {
490 array_push( $results, $this->wpmc_clean_url( $finalSetImg[0] ) );
491 }
492 }
493 }
494 }
495
496 // Links, href
497 $urls = $dom->getElementsByTagName( 'a' );
498 foreach ( $urls as $url ) {
499 $src = $this->wpmc_clean_url( $url->getAttribute('href') );
500 array_push( $results, $src );
501 }
502
503 // PDF
504 preg_match_all( "/((https?:\/\/)?[^\\&\#\[\] \"\?]+\.pdf)/", $html, $res );
505 if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 ) {
506 foreach ( $res[1] as $url ) {
507 error_log(print_r($url, 1));
508 array_push( $results, $this->wpmc_clean_url( $url ) );
509 }
510 }
511
512 // Background images
513 preg_match_all( "/url\(\'?\"?((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png))\'?\"?/", $html, $res );
514 if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 ) {
515 foreach ( $res[1] as $url ) {
516 //error_log(print_r($res, 1));
517 array_push( $results, $this->wpmc_clean_url( $url ) );
518 }
519 }
520
521 return $results;
522 }
523
524 // Parse a meta, visit all the arrays, look for the attributes, fill $ids and $urls arrays
525 function get_from_meta( $meta, $lookFor, &$ids, &$urls ) {
526 foreach ( $meta as $key => $value ) {
527 if ( is_object( $value ) || is_array( $value ) )
528 $this->get_from_meta( $value, $lookFor, $ids, $urls );
529 else if ( in_array( $key, $lookFor ) ) {
530 if ( empty( $value ) )
531 continue;
532 else if ( is_numeric( $value ) )
533 array_push( $ids, $value );
534 else
535 array_push( $urls, $this->wpmc_clean_url( $value ) );
536 }
537 }
538 }
539
540 function get_images_from_themes( &$ids, &$urls ) {
541 global $wpdb;
542
543 // USE CURRENT THEME AND WP API
544 $ch = get_custom_header();
545 if ( !empty( $ch ) && !empty( $ch->url ) ) {
546 array_push( $urls, $this->wpmc_clean_url( $ch->url ) );
547 }
548 if ( !empty( $ch ) && !empty( $ch->attachment_id ) ) {
549 array_push( $ids, $ch->attachment_id );
550 }
551 $cl = get_custom_logo();
552 if ( !empty( $cl ) ) {
553 $urls = array_merge( $this->get_urls_from_html( $cl ), $urls );
554 }
555 $cd = get_background_image();
556 if ( !empty( $cd ) ) {
557 array_push( $urls, $this->wpmc_clean_url( $cd ) );
558 }
559 $photography_hero_image = get_theme_mod( 'photography_hero_image' );
560 if ( !empty( $photography_hero_image ) ) {
561 array_push( $ids, $photography_hero_image );
562 }
563 $author_profile_picture = get_theme_mod( 'author_profile_picture' );
564 if ( !empty( $author_profile_picture ) ) {
565 array_push( $ids, $author_profile_picture );
566 }
567 }
568
569 function log( $data, $force = false ) {
570 if ( !get_option( 'wpmc_debuglogs', false ) && !$force )
571 return;
572 $fh = @fopen( trailingslashit( dirname(__FILE__) ) . '/media-cleaner.log', 'a' );
573 if ( !$fh )
574 return false;
575 $date = date( "Y-m-d H:i:s" );
576 fwrite( $fh, "$date: {$data}\n" );
577 fclose( $fh );
578 return true;
579 }
580
581 function wp_ajax_wpmc_scan() {
582 global $wpdb;
583
584 $method = get_option( 'wpmc_method', 'media' );
585 if ( !$this->admin->is_registered() )
586 $method = 'media';
587 $path = isset( $_POST['path'] ) ? $_POST['path'] : null;
588 $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
589 $limitsize = get_option( 'wpmc_medias_buffer', 100 );
590
591 if ( $method == 'files' ) {
592 $output = apply_filters( 'wpmc_list_uploaded_files', array(
593 'results' => array(), 'success' => false, 'message' => __( "Unavailable.", 'media-cleaner' )
594 ), $path );
595 echo json_encode( $output );
596 die();
597 }
598
599 if ( $method == 'media' ) {
600 // Prevent double scanning by removing filesystem entries that we have DB entries for
601 $results = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
602 WHERE p.post_status = 'inherit'
603 AND p.post_type = 'attachment'
604 LIMIT %d, %d", $limit, $limitsize
605 )
606 );
607 $finished = count( $results ) < $limitsize;
608 echo json_encode(
609 array(
610 'results' => $results,
611 'success' => true,
612 'finished' => $finished,
613 'limit' => $limit + $limitsize,
614 'message' => __( "Medias retrieved.", 'media-cleaner' ) )
615 );
616 die();
617 }
618
619 // No task.
620 echo json_encode( array( 'success' => false, 'message' => __( "No task.", 'media-cleaner' ) ) );
621 die();
622 }
623
624 /**
625 *
626 * HELPERS
627 *
628 */
629
630 function wpmc_trashdir() {
631 $upload_folder = wp_upload_dir();
632 return trailingslashit( $upload_folder['basedir'] ) . 'wpmc-trash';
633 }
634
635 /**
636 *
637 * DELETE / SCANNING / RESET
638 *
639 */
640
641 function wpmc_recover_file( $path ) {
642 $basedir = wp_upload_dir();
643 $originalPath = trailingslashit( $basedir['basedir'] ) . $path;
644 $trashPath = trailingslashit( $this->wpmc_trashdir() ) . $path;
645 $path_parts = pathinfo( $originalPath );
646 if ( !file_exists( $path_parts['dirname'] ) && !wp_mkdir_p( $path_parts['dirname'] ) ) {
647 die( 'Failed to create folder.' );
648 }
649 if ( !file_exists( $trashPath ) ) {
650 $this->log( "The file $originalPath actually does not exist in the trash." );
651 return true;
652 }
653 if ( !rename( $trashPath, $originalPath ) ) {
654 die( 'Failed to move the file.' );
655 }
656 return true;
657 }
658
659 function wpmc_recover( $id ) {
660 global $wpdb;
661 $table_name = $wpdb->prefix . "mclean_scan";
662 $issue = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $id ), OBJECT );
663 $issue->path = stripslashes( $issue->path );
664
665 // Files
666 if ( $issue->type == 0 ) {
667 $this->wpmc_recover_file( $issue->path );
668 $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 0 WHERE id = %d", $id ) );
669 return true;
670 }
671 // Media
672 else if ( $issue->type == 1 ) {
673
674 // Copy the main file back
675 $fullpath = get_attached_file( $issue->postId );
676 if ( empty( $fullpath ) ) {
677 error_log( "Media {$issue->postId} does not have attached file anymore." );
678 return false;
679 }
680 $mainfile = $this->wpmc_clean_uploaded_filename( $fullpath );
681 $baseUp = pathinfo( $mainfile );
682 $baseUp = $baseUp['dirname'];
683 $file = $this->wpmc_clean_uploaded_filename( $fullpath );
684 if ( !$this->wpmc_recover_file( $file ) ) {
685 $this->log( "Could not recover $file." );
686 error_log( "Media Cleaner: Could not recover $file." );
687 }
688
689 // If images, copy the other files as well
690 $meta = wp_get_attachment_metadata( $issue->postId );
691 $isImage = isset( $meta, $meta['width'], $meta['height'] );
692 $sizes = $this->wpmc_get_image_sizes();
693 if ( $isImage && isset( $meta['sizes'] ) ) {
694 foreach ( $meta['sizes'] as $name => $attr ) {
695 if ( isset( $attr['file'] ) ) {
696 $filepath = wp_upload_dir();
697 $filepath = $filepath['basedir'];
698 $filepath = trailingslashit( $filepath ) . trailingslashit( $baseUp ) . $attr['file'];
699 $file = $this->wpmc_clean_uploaded_filename( $filepath );
700 if ( !$this->wpmc_recover_file( $file ) ) {
701 $this->log( "Could not recover $file." );
702 error_log( "Media Cleaner: Could not recover $file." );
703 }
704 }
705 }
706 }
707 if ( !wp_untrash_post( $issue->postId ) ) {
708 error_log( "Cleaner: Failed to Untrash Post {$issue->postId} (but deleted it from Cleaner DB)." );
709 }
710 $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 0 WHERE id = %d", $id ) );
711 return true;
712 }
713 }
714
715 function wpmc_trash_file( $fileIssuePath ) {
716 global $wpdb;
717 $basedir = wp_upload_dir();
718 $originalPath = trailingslashit( $basedir['basedir'] ) . $fileIssuePath;
719 $trashPath = trailingslashit( $this->wpmc_trashdir() ) . $fileIssuePath;
720 $path_parts = pathinfo( $trashPath );
721
722 try {
723 if ( !file_exists( $path_parts['dirname'] ) && !wp_mkdir_p( $path_parts['dirname'] ) ) {
724 $this->log( "Could not create the trash directory for Media Cleaner." );
725 error_log( "Media Cleaner: Could not create the trash directory." );
726 return false;
727 }
728 // Rename the file (move). 'is_dir' is just there for security (no way we should move a whole directory)
729 if ( is_dir( $originalPath ) ) {
730 $this->log( "Attempted to delete a directory instead of a file ($originalPath). Can't do that." );
731 error_log( "Media Cleaner: Attempted to delete a directory instead of a file ($originalPath). Can't do that." );
732 return false;
733 }
734 if ( !file_exists( $originalPath ) ) {
735 $this->log( "The file $originalPath actually does not exist." );
736 return true;
737 }
738 if ( !@rename( $originalPath, $trashPath ) ) {
739 return false;
740 }
741 }
742 catch ( Exception $e ) {
743 return false;
744 }
745 $this->wpmc_clean_dir( dirname( $originalPath ) );
746 return true;
747 }
748
749 function wpmc_ignore( $id ) {
750 global $wpdb;
751 $table_name = $wpdb->prefix . "mclean_scan";
752 $has = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE id = %d AND ignored = 1", $id ) );
753 if ( $has > 0 )
754 $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET ignored = 0 WHERE id = %d", $id ) );
755 else
756 $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET ignored = 1 WHERE id = %d", $id ) );
757 return true;
758 }
759
760 function wpmc_endsWith( $haystack, $needle )
761 {
762 $length = strlen( $needle );
763 if ( $length == 0 )
764 return true;
765 return ( substr( $haystack, -$length ) === $needle );
766 }
767
768 function wpmc_clean_dir( $dir ) {
769 if ( !file_exists( $dir ) )
770 return;
771 else if ( $this->wpmc_endsWith( $dir, 'uploads' ) )
772 return;
773 $found = array_diff( scandir( $dir ), array( '.', '..' ) );
774 if ( count( $found ) < 1 ) {
775 if ( rmdir( $dir ) ) {
776 $this->wpmc_clean_dir( dirname( $dir ) );
777 }
778 }
779 }
780
781 function wpmc_delete( $id ) {
782 global $wpdb;
783 $table_name = $wpdb->prefix . "mclean_scan";
784 $issue = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $id ), OBJECT );
785 $regex = "^(.*)(\\s\\(\\+.*)$";
786 $issue->path = preg_replace( '/' . $regex . '/i', '$1', $issue->path ); // remove " (+ 6 files)" from path
787
788 // Make sure there isn't a media DB entry
789 if ( $issue->type == 0 ) {
790 $attachmentid = $this->wpmc_find_attachment_id_by_file( $issue->path );
791 if ( $attachmentid ) {
792 $this->log( "Issue listed as filesystem but Media {$attachmentid} exists." );
793 }
794 }
795
796 if ( $issue->type == 0 ) {
797
798 if ( $issue->deleted == 0 ) {
799 // Move file to the trash
800 if ( $this->wpmc_trash_file( $issue->path ) )
801 $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 1 WHERE id = %d", $id ) );
802 return true;
803 }
804 else {
805 // Delete file from the trash
806 $trashPath = trailingslashit( $this->wpmc_trashdir() ) . $issue->path;
807 if ( !unlink( $trashPath ) ) {
808 $this->log( "Failed to delete the file." );
809 error_log( "Media Cleaner: Failed to delete the file." );
810 }
811 $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE id = %d", $id ) );
812 $this->wpmc_clean_dir( dirname( $trashPath ) );
813 return true;
814 }
815 }
816
817 if ( $issue->type == 1 ) {
818 if ( $issue->deleted == 0 && MEDIA_TRASH ) {
819 // Move Media to trash
820 // Let's copy the images to the trash so that it can be recovered.
821 $fullpath = get_attached_file( $issue->postId );
822 $mainfile = $this->wpmc_clean_uploaded_filename( $fullpath );
823 $baseUp = pathinfo( $mainfile );
824 $baseUp = $baseUp['dirname'];
825 $file = $this->wpmc_clean_uploaded_filename( $fullpath );
826 if ( !$this->wpmc_trash_file( $file ) ) {
827 $this->log( "Could not trash $file." );
828 error_log( "Media Cleaner: Could not trash $file." );
829 return false;
830 }
831
832 // If images, check the other files as well
833 $meta = wp_get_attachment_metadata( $issue->postId );
834 $isImage = isset( $meta, $meta['width'], $meta['height'] );
835 $sizes = $this->wpmc_get_image_sizes();
836 if ( $isImage && isset( $meta['sizes'] ) ) {
837 foreach ( $meta['sizes'] as $name => $attr ) {
838 if ( isset( $attr['file'] ) ) {
839 $filepath = wp_upload_dir();
840 $filepath = $filepath['basedir'];
841 $filepath = trailingslashit( $filepath ) . trailingslashit( $baseUp ) . $attr['file'];
842 $file = $this->wpmc_clean_uploaded_filename( $filepath );
843 if ( !$this->wpmc_trash_file( $file ) ) {
844 $this->log( "Could not trash $file." );
845 error_log( "Media Cleaner: Could not trash $file." );
846 }
847 }
848 }
849 }
850 wp_delete_attachment( $issue->postId, false );
851 $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 1 WHERE id = %d", $id ) );
852 return true;
853 }
854 else {
855 // Trash Media definitely by recovering it (to be like a normal Media) and remove it through the
856 // standard WordPress workflow
857 if ( MEDIA_TRASH )
858 $this->wpmc_recover( $id );
859 wp_delete_attachment( $issue->postId, true );
860 $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE id = %d", $id ) );
861 return true;
862 }
863 }
864 return false;
865 }
866
867 /**
868 *
869 * SCANNING / RESET
870 *
871 */
872
873 function add_reference_url( $urlOrUrls, $type, $origin = null, $extra = null ) {
874 $urlOrUrls = !is_array( $urlOrUrls ) ? array( $urlOrUrls ) : $urlOrUrls;
875 foreach ( $urlOrUrls as $url ) {
876 // With files, we need both filename without resolution and filename with resolution, it's important
877 // to make sure the original file is not deleted if a size exists for it.
878 // With media, all URLs should be without resolution to make sure it matches Media.
879 if ( $this->current_method == 'files' )
880 $this->add_reference( null, $url, $type, $origin );
881 $this->add_reference( 0, $this->clean_url_from_resolution( $url ), $type, $origin );
882 }
883 }
884
885 function add_reference_id( $idOrIds, $type, $origin = null, $extra = null ) {
886 $idOrIds = !is_array( $idOrIds ) ? array( $idOrIds ) : $idOrIds;
887 foreach ( $idOrIds as $id )
888 $this->add_reference( $id, "", $type, $origin );
889 }
890
891 private $cached_ids = array();
892 private $cached_urls = array();
893
894 private function add_reference( $id, $url, $type, $origin = null, $extra = null ) {
895 // The references are actually not being added directly in the DB, they are being pushed
896 // into a cache ($this->refcache).
897 if ( !empty( $id ) ) {
898 if ( !in_array( $id, $this->cached_ids ) )
899 array_push( $this->cached_ids, $id );
900 else
901 return;
902 }
903 if ( !empty( $url ) ) {
904 // The URL shouldn't contain http, https, javascript at the beginning (and there are probably many more cases)
905 // The URL must be cleaned before being passed as a reference.
906 if ( substr( $url, 0, 5 ) === "http:" )
907 return;
908 if ( substr( $url, 0, 6 ) === "https:" )
909 return;
910 if ( substr( $url, 0, 11 ) === "javascript:" )
911 return;
912 if ( !in_array( $url, $this->cached_urls ) )
913 array_push( $this->cached_urls, $url );
914 else
915 return;
916 }
917 //
918 array_push( $this->refcache, array( 'id' => $id, 'url' => $url, 'type' => $type, 'origin' => $origin ) );
919
920 // Without cache, the code would be this.
921 // $wpdb->insert( $table_name,
922 // array(
923 // 'time' => current_time('mysql'), 'mediaId' => $id, 'mediaUrl' => $url, 'origin' => $origin, 'originType' => $type )
924 // );
925 }
926
927 // The cache containing the references is wrote to the DB.
928 function write_references() {
929 global $wpdb;
930 $table = $wpdb->prefix . "mclean_refs";
931 $values = array();
932 $place_holders = array();
933 $query = "INSERT INTO $table (mediaId, mediaUrl, originType) VALUES ";
934 foreach( $this->refcache as $key => $value ) {
935 array_push( $values, $value['id'], $value['url'], $value['type'] );
936 if ( get_option( 'wpmc_debuglogs', false ) ) {
937 if ( !empty( $value['id'] ) )
938 $this->log( "* {$value['type']}: Media #{$value['id']}" );
939 if ( !empty( $value['url'] ) )
940 $this->log( "* {$value['type']}: {$value['url']}" );
941 }
942 $place_holders[] = "('%d','%s','%s')";
943 }
944 if ( !empty( $values ) ) {
945 $query .= implode( ', ', $place_holders );
946 $prepared = $wpdb->prepare( "$query ", $values );
947 $wpdb->query( $prepared );
948 }
949 $this->refcache = array();
950 }
951
952 function wpmc_check_is_ignore( $file ) {
953 global $wpdb;
954 $table_name = $wpdb->prefix . "mclean_scan";
955 $count = $wpdb->get_var( "SELECT COUNT(*)
956 FROM $table_name
957 WHERE ignored = 1
958 AND path LIKE '%". esc_sql( $wpdb->esc_like( $file ) ) . "%'" );
959 if ( $count > 0 ) {
960 $this->log( "Could not trash $file." );
961 }
962 return ($count > 0);
963 }
964
965 function wpmc_find_attachment_id_by_file( $file ) {
966 global $wpdb;
967 $postmeta_table_name = $wpdb->prefix . 'postmeta';
968 $file = $this->wpmc_clean_uploaded_filename( $file );
969 $sql = $wpdb->prepare( "SELECT post_id
970 FROM {$postmeta_table_name}
971 WHERE meta_key = '_wp_attached_file'
972 AND meta_value = %s", $file
973 );
974 $ret = $wpdb->get_var( $sql );
975 if ( empty( $ret ) )
976 $this->log( "File $file not found as _wp_attached_file (Library)." );
977 else {
978 $this->log( "File $file found as Media $ret." );
979 }
980 return $ret;
981 }
982
983 function wpmc_get_image_sizes() {
984 $sizes = array();
985 global $_wp_additional_image_sizes;
986 foreach ( get_intermediate_image_sizes() as $s ) {
987 $crop = false;
988 if ( isset( $_wp_additional_image_sizes[$s] ) ) {
989 $width = intval( $_wp_additional_image_sizes[$s]['width'] );
990 $height = intval( $_wp_additional_image_sizes[$s]['height'] );
991 $crop = $_wp_additional_image_sizes[$s]['crop'];
992 } else {
993 $width = get_option( $s.'_size_w' );
994 $height = get_option( $s.'_size_h' );
995 $crop = get_option( $s.'_crop' );
996 }
997 $sizes[$s] = array( 'width' => $width, 'height' => $height, 'crop' => $crop );
998 }
999 return $sizes;
1000 }
1001
1002 function clean_url_from_resolution( $url ) {
1003 $pattern = '/[_-]\d+x\d+(?=\.[a-z]{3,4}$)/';
1004 $url = preg_replace( $pattern, '', $url );
1005 return $url;
1006 }
1007
1008 function clean_url_from_resolution_ref( &$url ) {
1009 $url = $this->clean_url_from_resolution( $url );
1010 }
1011
1012 // From a url to the shortened and cleaned url (for example '2013/02/file.png')
1013 function wpmc_clean_url( $url ) {
1014 $upload_folder = wp_upload_dir();
1015 $baseurl = $upload_folder['baseurl'];
1016 if ( substr( $url, 0, 2 ) === "//" )
1017 $url = "http:" . $url;
1018 $baseurl = str_replace( 'https://', 'http://', $baseurl );
1019 $baseurl = str_replace( 'http://www.', 'http://', $baseurl );
1020 $newurl = str_replace( 'https://', 'http://', $url );
1021 $newurl = str_replace( 'http://www.', 'http://', $newurl );
1022 $newurl = str_replace( $baseurl, '', $newurl );
1023 $newurl = trim( $newurl, "/" );
1024 return $newurl;
1025 }
1026
1027 // From a fullpath to the shortened and cleaned path (for example '2013/02/file.png')
1028 function wpmc_clean_uploaded_filename( $fullpath ) {
1029 $upload_folder = wp_upload_dir();
1030 $basedir = $upload_folder['basedir'];
1031 $file = str_replace( $basedir, '', $fullpath );
1032 $file = str_replace( "./", "", $file );
1033 $file = trim( $file, "/" );
1034 return $file;
1035 }
1036
1037 function wpmc_check_media( $attachmentId, $checkOnly = false ) {
1038
1039 $this->last_analysis = "N/A";
1040
1041 // Is it an image?
1042 $meta = wp_get_attachment_metadata( $attachmentId );
1043 $isImage = isset( $meta, $meta['width'], $meta['height'] );
1044
1045 // Get the main file
1046 global $wpdb;
1047 $fullpath = get_attached_file( $attachmentId );
1048 $mainfile = $this->wpmc_clean_uploaded_filename( $fullpath );
1049 $baseUp = pathinfo( $mainfile );
1050 $baseUp = $baseUp['dirname'];
1051 $size = 0;
1052 $countfiles = 0;
1053 $issue = 'NO_CONTENT';
1054 if ( file_exists( $fullpath ) ) {
1055
1056 // Special scan: Broken only!
1057 $check_postmeta = get_option( 'wpmc_postmeta', false );
1058 $check_posts = get_option( 'wpmc_posts', false );
1059 $check_widgets = get_option( 'wpmc_widgets', false );
1060 if ( !$check_postmeta && !$check_posts && !$check_widgets )
1061 return true;
1062
1063 $size = filesize( $fullpath );
1064
1065 // Analysis
1066 $this->last_analysis = "NONE";
1067 $this->log( "Checking Media #{$attachmentId}: {$mainfile}" );
1068 if ( $this->wpmc_check_is_ignore( $mainfile, $attachmentId ) ) {
1069 $this->last_analysis = "IGNORED";
1070 return true;
1071 }
1072 if ( $this->checkers->check( $mainfile, $attachmentId ) )
1073 return true;
1074
1075 // If images, check the other files as well
1076 $countfiles = 0;
1077 $sizes = $this->wpmc_get_image_sizes();
1078 if ( $isImage && isset( $meta['sizes'] ) ) {
1079 foreach ( $meta['sizes'] as $name => $attr ) {
1080 if ( isset( $attr['file'] ) ) {
1081 $filepath = wp_upload_dir();
1082 $filepath = $filepath['basedir'];
1083 $filepath = trailingslashit( $filepath ) . trailingslashit( $baseUp ) . $attr['file'];
1084 if ( file_exists( $filepath ) )
1085 $size += filesize( $filepath );
1086 $file = $this->wpmc_clean_uploaded_filename( $filepath );
1087 $countfiles++;
1088 // Analysis
1089 $this->log( "Checking Media #{$attachmentId}: {$file}" );
1090 if ( $this->checkers->check( $mainfile, $attachmentId ) )
1091 return true;
1092 }
1093 }
1094 }
1095 } else {
1096 $this->log( "File {$fullpath} does not exist." );
1097 $issue = 'ORPHAN_MEDIA';
1098 }
1099
1100 if ( !$checkOnly ) {
1101 $table_name = $wpdb->prefix . "mclean_scan";
1102 $wpdb->insert( $table_name,
1103 array(
1104 'time' => current_time('mysql'),
1105 'type' => 1,
1106 'size' => $size,
1107 'path' => $mainfile . ( $countfiles > 0 ? ( " (+ " . $countfiles . " files)" ) : "" ),
1108 'postId' => $attachmentId,
1109 'issue' => $issue
1110 )
1111 );
1112 }
1113 return false;
1114 }
1115
1116 // Delete all issues
1117 function wpmc_reset_issues( $includingIgnored = false ) {
1118 global $wpdb;
1119 $table_name = $wpdb->prefix . "mclean_scan";
1120 if ( $includingIgnored ) {
1121 $wpdb->query( "DELETE FROM $table_name WHERE deleted = 0" );
1122 }
1123 else {
1124 $wpdb->query( "DELETE FROM $table_name WHERE ignored = 0 AND deleted = 0" );
1125 }
1126 if ( file_exists( plugin_dir_path( __FILE__ ) . '/media-cleaner.log' ) ) {
1127 file_put_contents( plugin_dir_path( __FILE__ ) . '/media-cleaner.log', '' );
1128 }
1129 $table_name = $wpdb->prefix . "mclean_refs";
1130 $wpdb->query("TRUNCATE $table_name");
1131
1132 // Delete the old transient. Let's delete those lines later.
1133 delete_transient( "wpmc_theme_ids" );
1134 delete_transient( "wpmc_theme_urls" );
1135 delete_transient( "wpmc_widgets_ids" );
1136 delete_transient( "wpmc_widgets_urls" );
1137 delete_transient( "wpmc_posts_images_urls" );
1138 delete_transient( "wpmc_posts_images_ids" );
1139 delete_transient( "wpmc_postmeta_images_urls" );
1140 delete_transient( "wpmc_postmeta_images_ids" );
1141 delete_transient( "wpmc_postmeta_images_acf_urls" );
1142 delete_transient( "wpmc_postmeta_images_acf_ids" );
1143 delete_transient( "wpmc_posts_images_visualcomposer" );
1144 delete_transient( "wpmc_galleries_images_visualcomposer" );
1145 delete_transient( "wpmc_galleries_images_fusionbuilder" );
1146 delete_transient( "wpmc_galleries_images_woocommerce" );
1147 delete_transient( "wpmc_galleries_images_divi" );
1148 delete_transient( "wpmc_galleries_images_urls" );
1149 }
1150
1151 /**
1152 *
1153 * DASHBOARD
1154 *
1155 */
1156
1157 function admin_inline_js() {
1158 echo "<script type='text/javascript'>\n";
1159 echo 'var wpmc_cfg = {
1160 delay: ' . get_option( 'wpmc_delay', 100 ) . ',
1161 analysisBuffer: ' . get_option( 'wpmc_analysis_buffer', 50 ) . ',
1162 isPro: ' . ( $this->admin->is_registered() ? '1' : '0') . ',
1163 scanFiles: ' . ( ( get_option( 'wpmc_method', 'media' ) == 'files' && $this->admin->is_registered() ) ? '1' : '0' ) . ',
1164 scanMedia: ' . ( get_option( 'wpmc_method', 'media' ) == 'media' ? '1' : '0' ) . ' };';
1165 echo "\n</script>";
1166 }
1167
1168 function echo_issue( $issue ) {
1169 if ( $issue == 'NO_CONTENT' ) {
1170 _e( "Seems not in use.", 'media-cleaner' );
1171 }
1172 else if ( $issue == 'NO_MEDIA' ) {
1173 _e( "Not in Media Library.", 'media-cleaner' );
1174 }
1175 else if ( $issue == 'ORPHAN_RETINA' ) {
1176 _e( "Orphan retina.", 'media-cleaner' );
1177 }
1178 else if ( $issue == 'ORPHAN_MEDIA' ) {
1179 _e( "File not found.", 'media-cleaner' );
1180 }
1181 else {
1182 echo $issue;
1183 }
1184 }
1185
1186 function media_row_actions( $actions, $post ) {
1187 global $current_screen;
1188 if ( 'upload' != $current_screen->id )
1189 return $actions;
1190 global $wpdb;
1191 $table_name = $wpdb->prefix . "mclean_scan";
1192 $res = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE postId = %d", $post->ID ) );
1193 if ( !empty( $res ) && isset( $actions['delete'] ) )
1194 $actions['delete'] = "<a href='?page=media-cleaner&view=deleted'>" .
1195 __( 'Delete with Media Cleaner', 'media-cleaner' ) . "</a>";
1196 if ( !empty( $res ) && isset( $actions['trash'] ) )
1197 $actions['trash'] = "<a href='?page=media-cleaner'>" .
1198 __( 'Trash with Media Cleaner', 'media-cleaner' ) . "</a>";
1199 if ( !empty( $res ) && isset( $actions['untrash'] ) ) {
1200 $actions['untrash'] = "<a href='?page=media-cleaner&view=deleted'>" .
1201 __( 'Restore with Media Cleaner', 'media-cleaner' ) . "</a>";
1202 }
1203 return $actions;
1204 }
1205
1206 function wpmc_screen() {
1207 global $wplr;
1208 ?>
1209 <div class='wrap'>
1210
1211 <?php
1212 echo $this->admin->display_title( "Media Cleaner" );
1213 global $wpdb;
1214 $posts_per_page = get_user_meta( get_current_user_id(), 'upload_per_page', true );
1215 if ( empty( $posts_per_page ) )
1216 $posts_per_page = 20;
1217 $view = isset ( $_GET[ 'view' ] ) ? sanitize_text_field( $_GET[ 'view' ] ) : "issues";
1218 $paged = isset ( $_GET[ 'paged' ] ) ? sanitize_text_field( $_GET[ 'paged' ] ) : 1;
1219 $reset = isset ( $_GET[ 'reset' ] ) ? $_GET[ 'reset' ] : 0;
1220 if ( $reset ) {
1221 wpmc_reset();
1222 $this->wpmc_reset_issues();
1223 }
1224 $s = isset ( $_GET[ 's' ] ) ? sanitize_text_field( $_GET[ 's' ] ) : null;
1225 $table_name = $wpdb->prefix . "mclean_scan";
1226 $issues_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE ignored = 0 AND deleted = 0" );
1227 $total_size = $wpdb->get_var( "SELECT SUM(size) FROM $table_name WHERE ignored = 0 AND deleted = 0" );
1228 $trash_total_size = $wpdb->get_var( "SELECT SUM(size) FROM $table_name WHERE ignored = 0 AND deleted = 1" );
1229 $ignored_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE ignored = 1" );
1230 $deleted_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE deleted = 1" );
1231
1232 if ( $view == 'deleted' ) {
1233 $items_count = $deleted_count;
1234 $items = $wpdb->get_results( $wpdb->prepare( "SELECT id, type, postId, path, size, ignored, deleted, issue
1235 FROM $table_name WHERE ignored = 0 AND deleted = 1 AND path LIKE %s
1236 ORDER BY time
1237 DESC LIMIT %d, %d", '%' . $s . '%', ( $paged - 1 ) * $posts_per_page, $posts_per_page ), OBJECT );
1238 }
1239 else if ( $view == 'ignored' ) {
1240 $items_count = $ignored_count;
1241 $items = $wpdb->get_results( $wpdb->prepare( "SELECT id, type, postId, path, size, ignored, deleted, issue
1242 FROM $table_name
1243 WHERE ignored = 1 AND deleted = 0 AND path LIKE %s
1244 ORDER BY time
1245 DESC LIMIT %d, %d", '%' . $s . '%', ( $paged - 1 ) * $posts_per_page, $posts_per_page ), OBJECT );
1246 }
1247 else {
1248 $items_count = $issues_count;
1249 $items = $wpdb->get_results( $wpdb->prepare( "SELECT id, type, postId, path, size, ignored, deleted, issue
1250 FROM $table_name
1251 WHERE ignored = 0 AND deleted = 0 AND path LIKE %s
1252 ORDER BY time
1253 DESC LIMIT %d, %d", '%' . $s . '%', ( $paged - 1 ) * $posts_per_page, $posts_per_page ), OBJECT );
1254 }
1255 ?>
1256
1257 <style>
1258 #wpmc-pages {
1259 float: right;
1260 position: relative;
1261 top: 12px;
1262 }
1263
1264 #wpmc-pages a {
1265 text-decoration: none;
1266 border: 1px solid black;
1267 padding: 2px 5px;
1268 border-radius: 4px;
1269 background: #E9E9E9;
1270 color: lightslategrey;
1271 border-color: #BEBEBE;
1272 }
1273
1274 #wpmc-pages .current {
1275 font-weight: bold;
1276 }
1277 </style>
1278
1279 <div style='margin-top: 0px; background: #FFF; padding: 5px; border-radius: 4px; height: 28px; box-shadow: 0px 0px 6px #C2C2C2;'>
1280
1281 <!-- SCAN -->
1282 <?php if ( $view != 'deleted' ) { ?>
1283 <a id='wpmc_scan' onclick='wpmc_scan()' class='button-primary' style='float: left;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-search"></span><?php _e("Start Scan", 'media-cleaner'); ?></a>
1284 <?php } ?>
1285
1286 <!-- PAUSE -->
1287 <?php if ( $view != 'deleted' ) { ?>
1288 <a id='wpmc_pause' onclick='wpmc_pause()' class='button' style='float: left; margin-left: 5px; display: none;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-controls-pause"></span><?php _e("Pause", 'media-cleaner'); ?></a>
1289 <?php } ?>
1290
1291 <!-- DELETE SELECTED -->
1292 <a id='wpmc_delete' onclick='wpmc_delete()' class='button' style='float: left; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-no"></span><?php _e("Delete", 'media-cleaner'); ?></a>
1293 <?php if ( $view == 'deleted' ) { ?>
1294 <a id='wpmc_recover' onclick='wpmc_recover()' class='button-secondary' style='float: left; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-controls-repeat"></span><?php _e( "Recover", 'media-cleaner' ); ?></a>
1295 <?php } ?>
1296
1297 <!-- IGNORE SELECTED -->
1298 <a id='wpmc_ignore' onclick='wpmc_ignore()' class='button' style='float: left; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-yes"></span><?php
1299 if ( $view == 'ignored' )
1300 _e( "Mark as Issue", 'media-cleaner' );
1301 else
1302 _e( "Ignore", 'media-cleaner' );
1303 ?>
1304 </a>
1305
1306 <!-- RESET -->
1307 <?php if ( $view != 'deleted' ) { ?>
1308 <a id='wpmc_reset' href='?page=media-cleaner&reset=1' class='button-primary' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-sos"></span><?php _e("Reset", 'media-cleaner'); ?></a>
1309 <?php } ?>
1310
1311 <!-- DELETE ALL -->
1312 <?php if ( $view == 'deleted' ) { ?>
1313 <a id='wpmc_recover_all' onclick='wpmc_recover_all()' class='button-primary' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-controls-repeat"></span><?php _e("Recover all", 'media-cleaner'); ?></a>
1314 <a id='wpmc_delete_all' onclick='wpmc_delete_all(true)' class='button button-red' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-trash"></span><?php _e("Empty trash", 'media-cleaner'); ?></a>
1315 <?php } else { ?>
1316 <a id='wpmc_delete_all' onclick='wpmc_delete_all()' class='button button-red' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-trash"></span><?php _e("Delete all", 'media-cleaner'); ?></a>
1317 <?php } ?>
1318
1319 <form id="posts-filter" action="upload.php" method="get" style='float: right;'>
1320 <p class="search-box" style='margin-left: 5px; float: left;'>
1321 <input type="search" name="s" style="width: 120px;" value="<?php echo $s ? $s : ""; ?>">
1322 <input type="hidden" name="page" value="media-cleaner">
1323 <input type="hidden" name="view" value="<?php echo $view; ?>">
1324 <input type="hidden" name="paged" value="1">
1325 <input type="submit" class="button" value="<?php _e( 'Search', 'media-cleaner' ) ?>"><span style='border-right: #A2A2A2 solid 1px; margin-left: 5px; margin-right: 3px;'>&nbsp;</span>
1326 </p>
1327 </form>
1328
1329 <!-- PROGRESS -->
1330 <span style='margin-left: 12px; font-size: 15px; top: 5px; position: relative; color: #747474;' id='wpmc_progression'></span>
1331
1332 </div>
1333
1334 <p>
1335 <?php
1336
1337 $table_scan = $wpdb->prefix . "mclean_scan";
1338 $table_refs = $wpdb->prefix . "mclean_refs";
1339 if ( $wpdb->get_var("SHOW TABLES LIKE '$table_scan'") != $table_scan ||
1340 $wpdb->get_var("SHOW TABLES LIKE '$table_refs'") != $table_refs ) {
1341 _e( "<div class='notice notice-error'><p><b>The database is not ready for Media Cleaner. The scan will not work.</b> Click on the <b>Reset</b> button, it re-creates the tables required by Media Cleaner. If this message still appear, contact the support.</p></div>", 'media-cleaner' );
1342 }
1343 else {
1344 $method = get_option( 'wpmc_method', 'media' );
1345 if ( !$this->admin->is_registered() )
1346 $method = 'media';
1347
1348 $hide_warning = get_option( 'wpmc_hide_warning', false );
1349
1350 if ( !$hide_warning ) {
1351 _e( "<div class='notice notice-warning'><p><b style='color: red;'>Important.</b> <b>Backup your DB and your /uploads directory before using Media Cleaner. </b> The deleted files will be temporarily moved to the <b>uploads/wpmc-trash</b> directory. After testing your website, you can check the <a href='?page=media-cleaner&s&view=deleted'>trash</a> to either empty it or recover the media and files. The Media Cleaner does its best to be safe to use. However, WordPress being a very dynamic and pluggable system, it is impossible to predict all the situations in which your files are used. <b style='color: red;'>Again, please backup!</b> If you don't know how, give a try to this: <a href='https://updraftplus.com/?afref=460' target='_blank'>UpdraftPlus</a>. <br /><br /><b style='color: red;'>Be thoughtful.</b> Don't blame Media Cleaner if it deleted too many or not enough of your files. It makes cleaning possible and this task is only possible this way; don't post a bad review because it broke your install. <b>If you have a proper backup, there is no risk</b>. Sorry for the lengthy message, but better be safe than sorry. You can disable this big warning in the options if you have a Pro license. Make sure you read this warning twice. Media Cleaner is awesome and always getting better so I hope you will enjoy it. Thank you :)</p></div>", 'media-cleaner' );
1352 }
1353
1354 if ( !MEDIA_TRASH ) {
1355 _e( "<div class='notice notice-warning'><p>The trash for the Media Library is disabled. Any media removed by the plugin will be <b>permanently deleted</b>. To enable it, modify your wp-config.php file and add this line (preferably at the top):<br /><b>define( 'MEDIA_TRASH', true );</b></p></div>", 'media-cleaner' );
1356 }
1357 }
1358
1359 if ( !$this->admin->is_registered() ) {
1360 echo "<div class='notice notice-info'><p>";
1361 _e( "<b>This version is not Pro.</b> This plugin is a lot of work so please consider <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a> in order to receive support and to contribute in the evolution of it. Also, <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a> version will also give you the option <b>to scan the physical files in your /uploads folder</b> and extra checks for the common Page Builders.", 'media-cleaner' );
1362 echo "</p></div>";
1363
1364 if ( function_exists( '_et_core_find_latest' ) ) {
1365 echo "<div class='notice notice-warning'><p>";
1366 _e( "<b>Divi has been detected</b>. The free version might detect the files used by Divi correctly, but its full support is only available in <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a>.", 'media-cleaner' );
1367 echo "</p></div>";
1368 }
1369
1370 if ( class_exists( 'Vc_Manager' ) ) {
1371 echo "<div class='notice notice-warning'><p>";
1372 _e( "<b>Visual Composer has been detected</b>. The free version might detect the files used by Visual Composer correctly, but its full support is only available in <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a>.", 'media-cleaner' );
1373 echo "</p></div>";
1374 }
1375
1376 if ( function_exists( 'fusion_builder_map' ) ) {
1377 echo "<div class='notice notice-warning'><p>";
1378 _e( "<b>Fusion Builder has been detected</b>. The free version might detect the files used by Fusion Builder correctly, but its full support is only available in <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a>.", 'media-cleaner' );
1379 echo "</p></div>";
1380 }
1381
1382 if ( class_exists( 'FLBuilderModel' ) ) {
1383 echo "<div class='notice notice-warning'><p>";
1384 _e( "<b>Beaver Builder has been detected</b>. The free version might detect the files used by Beaver Builder correctly, but its full support is only available in <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a>.", 'media-cleaner' );
1385 echo "</p></div>";
1386 }
1387
1388 if ( function_exists( 'elementor_load_plugin_textdomain' ) ) {
1389 echo "<div class='notice notice-warning'><p>";
1390 _e( "<b>Elementor has been detected</b>. The free version might detect the files used by Elementor correctly, but its full support is only available in <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a>.", 'media-cleaner' );
1391 echo "</p></div>";
1392 }
1393
1394 if ( class_exists( 'SiteOrigin_Panels' ) ) {
1395 echo "<div class='notice notice-warning'><p>";
1396 _e( "<b>SiteOrigin Page Builder has been detected</b>. The free version might detect the files used by SiteOrigin Page Builder correctly, but its full support is only available in <a target='_blank' href='//meowapps.com/media-cleaner'>Media Cleaner Pro</a>.", 'media-cleaner' );
1397 echo "</p></div>";
1398 }
1399
1400 }
1401
1402 $anychecks = get_option( 'wpmc_posts', false ) || get_option( 'wpmc_postmeta', false ) || get_option( 'wpmc_widgets', false );
1403 $check_library = get_option(' wpmc_media_library', true );
1404
1405 if ( $method == 'media' ) {
1406 if ( !$anychecks )
1407 _e( "<div class='error'><p>Media Cleaner will analyze your Media Library. However, There is <b>NOTHING MARKED TO BE CHECKED</b> in the Settings. Media Cleaner will therefore run a special scan: <b>only the broken medias will be detected as issues.</b></p></div>", 'media-cleaner' );
1408 else
1409 _e( "<div class='notice notice-success'><p>Media Cleaner will analyze your Media Library.</p></div>", 'media-cleaner' );
1410 }
1411 else if ( $method == 'files' ) {
1412 if ( !$anychecks && !$check_library )
1413 _e( "<div class='error'><p>Media Cleaner will analyze your Filesystem. However, There is <b>NOTHING MARKED TO BE CHECKED</b> in the Settings. If you scan now, all the files will be detected as <b>NOT USED</b>.</p></div>", 'media-cleaner' );
1414 else
1415 _e( "<div class='notice notice-success'><p>Media Cleaner will analyze your Filesystem.</p></div>", 'media-cleaner' );
1416 }
1417
1418 echo sprintf( __( 'There are <b>%s issue(s)</b> with your files, accounting for <b>%s MB</b>. Your trash contains <b>%s MB.</b>', 'media-cleaner' ), number_format( $issues_count, 0 ), number_format( $total_size / 1000000, 2 ), number_format( $trash_total_size / 1000000, 2 ) );
1419 ?>
1420 </p>
1421
1422 <div id='wpmc-pages'>
1423 <?php
1424 echo paginate_links(array(
1425 'base' => '?page=media-cleaner&s=' . urlencode($s) . '&view=' . $view . '%_%',
1426 'current' => $paged,
1427 'format' => '&paged=%#%',
1428 'total' => ceil( $items_count / $posts_per_page ),
1429 'prev_next' => false
1430 ));
1431 ?>
1432 </div>
1433
1434 <ul class="subsubsub">
1435 <li class="all"><a <?php if ( $view == 'issues' ) echo "class='current'"; ?> href='?page=media-cleaner&s=<?php echo $s; ?>&view=issues'><?php _e( "Issues", 'media-cleaner' ); ?></a><span class="count">(<?php echo $issues_count; ?>)</span></li> |
1436 <li class="all"><a <?php if ( $view == 'ignored' ) echo "class='current'"; ?> href='?page=media-cleaner&s=<?php echo $s; ?>&view=ignored'><?php _e( "Ignored", 'media-cleaner' ); ?></a><span class="count">(<?php echo $ignored_count; ?>)</span></li> |
1437 <li class="all"><a <?php if ( $view == 'deleted' ) echo "class='current'"; ?> href='?page=media-cleaner&s=<?php echo $s; ?>&view=deleted'><?php _e( "Trash", 'media-cleaner' ); ?></a><span class="count">(<?php echo $deleted_count; ?>)</span></li>
1438 </ul>
1439
1440 <table id='wpmc-table' class='wp-list-table widefat fixed media'>
1441
1442 <thead>
1443 <tr>
1444 <th scope="col" id="cb" class="manage-column column-cb check-column"><input id="wpmc-cb-select-all" type="checkbox"></th>
1445 <?php if ( !get_option( 'wpmc_hide_thumbnails', false ) ): ?>
1446 <th style='width: 64px;'><?php _e( 'Thumb', 'media-cleaner' ) ?></th>
1447 <?php endif; ?>
1448 <th style='width: 50px;'><?php _e( 'Type', 'media-cleaner' ) ?></th>
1449 <th style='width: 80px;'><?php _e( 'Origin', 'media-cleaner' ) ?></th>
1450
1451 <?php if ( !empty( $wplr ) ): ?>
1452 <th style='width: 70px;'><?php _e( 'LR ID', 'media-cleaner' ) ?></th>
1453 <?php endif; ?>
1454
1455 <th><?php _e( 'Path', 'media-cleaner' ) ?></th>
1456 <th style='width: 220px;'><?php _e( 'Issue', 'media-cleaner' ) ?></th>
1457 <th style='width: 80px; text-align: right;'><?php _e( 'Size', 'media-cleaner' ) ?></th>
1458 </tr>
1459 </thead>
1460
1461 <tbody>
1462 <?php
1463 foreach ( $items as $issue ) {
1464 $regex = "^(.*)(\\s\\(\\+.*)$";
1465 $issue->path = preg_replace( '/' .$regex . '/i', '$1', $issue->path );
1466 ?>
1467 <tr>
1468 <td><input type="checkbox" name="id" value="<?php echo $issue->id ?>"></td>
1469 <?php if ( !get_option( 'wpmc_hide_thumbnails', false ) ): ?>
1470 <td>
1471 <?php
1472 if ( $issue->deleted == 0 ) {
1473 if ( $issue ->type == 0 ) {
1474 // FILE
1475 $upload_dir = wp_upload_dir();
1476 $url = htmlspecialchars( $upload_dir['baseurl'] . '/' . $issue->path, ENT_QUOTES );
1477 echo "<a target='_blank' href='" . $url .
1478 "'><img style='max-width: 48px; max-height: 48px;' src='" . $url . "' /></a>";
1479 }
1480 else {
1481 // MEDIA
1482 $file = get_attached_file( $issue->postId );
1483 if ( file_exists( $file ) ) {
1484 $attachmentsrc = wp_get_attachment_image_src( $issue->postId, 'thumbnail' );
1485 if ( empty( $attachmentsrc ) )
1486 echo '<span class="dashicons dashicons-no-alt"></span>';
1487 else {
1488 $attachmentsrc_clean = htmlspecialchars( $attachmentsrc[0], ENT_QUOTES );
1489 echo "<a target='_blank' href='" . $attachmentsrc_clean .
1490 "'><img style='max-width: 48px; max-height: 48px;' src='" .
1491 $attachmentsrc_clean . "' />";
1492 }
1493 }
1494 else {
1495 echo '<span class="dashicons dashicons-no-alt"></span>';
1496 }
1497 }
1498 }
1499 if ( $issue->deleted == 1 ) {
1500 $upload_dir = wp_upload_dir();
1501 $url = htmlspecialchars( $upload_dir['baseurl'] . '/wpmc-trash/' . $issue->path, ENT_QUOTES );
1502 echo "<a target='_blank' href='" . $url .
1503 "'><img style='max-width: 48px; max-height: 48px;' src='" . $url . "' /></a>";
1504 }
1505 ?>
1506 </td>
1507 <?php endif; ?>
1508 <td><?php echo $issue->type == 0 ? 'FILE' : 'MEDIA'; ?></td>
1509 <td><?php echo $issue->type == 0 ? 'Filesystem' : ("<a href='post.php?post=" .
1510 $issue->postId . "&action=edit'>ID " . $issue->postId . "</a>"); ?></td>
1511 <?php if ( !empty( $wplr ) ) { $info = $wplr->get_sync_info( $issue->postId ); ?>
1512 <td style='width: 70px;'><?php echo ( !empty( $info ) && $info->lr_id ? $info->lr_id : "" ); ?></td>
1513 <?php } ?>
1514 <td><?php echo stripslashes( $issue->path ); ?></td>
1515 <td><?php $this->echo_issue( $issue->issue ); ?></td>
1516 <td style='text-align: right;'><?php echo number_format( $issue->size / 1000, 2 ); ?> KB</td>
1517 </tr>
1518 <?php } ?>
1519 </tbody>
1520
1521 <tfoot>
1522 <tr><th></th>
1523 <?php if ( !get_option( 'hide_thumbnails', false ) ): ?>
1524 <th></th>
1525 <?php endif; ?>
1526 <th><?php _e( 'Type', 'media-cleaner' ) ?></th><th><?php _e( 'Origin', 'media-cleaner' ) ?></th>
1527 <?php if ( !empty( $wplr ) ): ?>
1528 <th style='width: 70px;'><?php _e( 'LR ID', 'media-cleaner' ) ?></th>
1529 <?php endif; ?>
1530 <th><?php _e( 'Path', 'media-cleaner' ) ?></th><th><?php _e( 'Issue', 'media-cleaner' ) ?></th><th style='width: 80px; text-align: right;'><?php _e( 'Size', 'media-cleaner' ) ?></th></tr>
1531 </tfoot>
1532
1533 </table>
1534 </wrap>
1535
1536 <?php
1537 }
1538
1539 function admin_menu() {
1540 //load_plugin_textdomain( 'media-cleaner', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
1541 add_media_page( 'Media Cleaner', 'Cleaner', 'manage_options', 'media-cleaner', array( $this, 'wpmc_screen' ) );
1542 }
1543
1544 function wp_enqueue_scripts () {
1545 wp_enqueue_style( 'media-cleaner-css', plugins_url( '/media-cleaner.css', __FILE__ ) );
1546 wp_enqueue_script( 'media-cleaner', plugins_url( '/media-cleaner.js', __FILE__ ), array( 'jquery' ), "3.7.0", true );
1547 }
1548 }
1549
1550
1551 /*
1552 INSTALL / UNINSTALL
1553 */
1554
1555 register_activation_hook( __FILE__, 'wpmc_reset' );
1556 register_deactivation_hook( __FILE__, 'wpmc_uninstall' );
1557 register_uninstall_hook( __FILE__, 'wpmc_uninstall' );
1558
1559 function wpmc_reset () {
1560 wpmc_uninstall();
1561 wpmc_install();
1562 $upload_folder = wp_upload_dir();
1563 $basedir = $upload_folder['basedir'];
1564 if ( !is_writable( $basedir ) ) {
1565 echo '<div class="error"><p>' . __( 'The directory for uploads is not writable. Media Cleaner will only be able to scan.', 'media-cleaner' ) . '</p></div>';
1566 }
1567
1568 }
1569
1570 function wpmc_install() {
1571 global $wpdb;
1572 $table_name = $wpdb->prefix . "mclean_scan";
1573 $charset_collate = $wpdb->get_charset_collate();
1574 $sql = "CREATE TABLE $table_name (
1575 id BIGINT(20) NOT NULL AUTO_INCREMENT,
1576 time DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL,
1577 type TINYINT(1) NOT NULL,
1578 postId BIGINT(20) NULL,
1579 path TINYTEXT NULL,
1580 size INT(9) NULL,
1581 ignored TINYINT(1) NOT NULL DEFAULT 0,
1582 deleted TINYINT(1) NOT NULL DEFAULT 0,
1583 issue TINYTEXT NOT NULL,
1584 PRIMARY KEY (id)
1585 ) " . $charset_collate . ";";
1586 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1587 dbDelta( $sql );
1588 $table_name = $wpdb->prefix . "mclean_refs";
1589 $charset_collate = $wpdb->get_charset_collate();
1590 // This key doesn't work on too many installs because of the 'Specified key was too long' issue
1591 // KEY mediaLookUp (mediaId, mediaUrl)
1592 $sql = "CREATE TABLE $table_name (
1593 id BIGINT(20) NOT NULL AUTO_INCREMENT,
1594 mediaId BIGINT(20) NULL,
1595 mediaUrl VARCHAR(256) NULL,
1596 originType VARCHAR(32) NOT NULL,
1597 PRIMARY KEY (id)
1598 ) " . $charset_collate . ";";
1599 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1600 dbDelta( $sql );
1601 }
1602
1603 function wpmc_uninstall () {
1604 global $wpdb;
1605 $table_name = $wpdb->prefix . "wpmcleaner";
1606 $wpdb->query("DROP TABLE IF EXISTS $table_name");
1607 $table_name = $wpdb->prefix . "mclean_scan";
1608 $wpdb->query("DROP TABLE IF EXISTS $table_name");
1609 $table_name = $wpdb->prefix . "mclean_refs";
1610 $wpdb->query("DROP TABLE IF EXISTS $table_name");
1611 }
1612