Media Cleaner - Version 5.2.0

Version Description

  • Update: Many optimizations, modules and big sections of the code are now only loaded when really needed.
  • Fix: Filenames with spaces weren't detected correctly and other.
  • Fix: Make sure that the shortcodes are resolved.
  • Add: Compatibility with more plugins (ACF Widgets, Attachments, Metaslider).
  • Info: Mike Meinz is an amazing developer who made a thorough debugging of the whole process, made a lot of corrections, and added support for more plugins. Thanks to him, Media Cleaner is much better!
Download this release

Release Info

Developer TigrouMeow
Plugin Icon 128x128 Media Cleaner
Version 5.2.0
Comparing to
See all releases

Code changes from version 5.1.3 to 5.2.0

core.php CHANGED
@@ -14,16 +14,20 @@ class Meow_WPMC_Core {
14
  $this->admin = $admin;
15
  if ( is_admin() )
16
  $this->admin_init();
 
 
 
 
 
 
 
 
17
  }
18
 
19
  function admin_init() {
20
  $this->current_method = get_option( 'wpmc_method', 'media' );
21
  $types = "jpg|jpeg|jpe|gif|png|tiff|bmp|csv|pdf|xls|xlsx|doc|docx|tiff|mp3|mp4|wav|lua";
22
  $this->regex_file = str_replace( "MIMETYPES", $types, $this->regex_file );
23
- require( 'scan.php' );
24
- require( 'checkers.php' );
25
- new MeowApps_WPMC_Scan( $this );
26
- $this->checkers = new Meow_WPMC_Checkers( $this );
27
  }
28
 
29
  function deepsleep( $seconds ) {
@@ -105,39 +109,12 @@ class Meow_WPMC_Core {
105
  }
106
  }
107
 
108
- function get_images_from_widgets( &$ids, &$urls ) {
109
- global $wp_registered_widgets;
110
- $syswidgets = $wp_registered_widgets;
111
- $active_widgets = get_option( 'sidebars_widgets' );
112
- foreach ( $active_widgets as $sidebar_name => $widgets ) {
113
- if ( $sidebar_name != 'wp_inactive_widgets' && !empty( $widgets ) && is_array( $widgets ) ) {
114
- $i = 0;
115
- foreach ( $widgets as $key => $widget ) {
116
- $widget_class = $syswidgets[$widget]['callback'][0]->option_name;
117
- $instance_id = $syswidgets[$widget]['params'][0]['number'];
118
- $widget_data = get_option( $widget_class );
119
- // error_log( "INSTANCE $key ($instance_id)" );
120
- // error_log( print_r( $widget_data, 1 ) );
121
- if ( !empty( $widget_data[$instance_id]['text'] ) ) {
122
- $html = do_shortcode( $widget_data[$instance_id]['text'] );
123
- $urls = array_merge( $urls, $this->get_urls_from_html( $html ) );
124
- }
125
- if ( !empty( $widget_data[$instance_id]['attachment_id'] ) ) {
126
- $id = $widget_data[$instance_id]['attachment_id'];
127
- array_push( $ids, $id );
128
- }
129
- if ( !empty( $widget_data[$instance_id]['url'] ) ) {
130
- $url = $this->wpmc_clean_url( $widget_data[$instance_id]['url'] );
131
- array_push( $urls, $url );
132
- }
133
- if ( !empty( $widget_data[$instance_id]['ids'] ) ) {
134
- $newIds = $widget_data[$instance_id]['ids'];
135
- $ids = array_merge( $ids, $newIds );
136
- }
137
- $i++;
138
- }
139
- }
140
- }
141
  }
142
 
143
  function get_urls_from_html( $html ) {
@@ -145,15 +122,19 @@ class Meow_WPMC_Core {
145
  return array();
146
  libxml_use_internal_errors( false );
147
 
 
 
 
 
148
  $dom = new DOMDocument();
149
- @$dom->loadHTML( $html );
150
 
151
  // Images, src, srcset
152
  $imgs = $dom->getElementsByTagName( 'img' );
153
  $results = array();
154
  foreach ( $imgs as $img ) {
155
  $src = $this->wpmc_clean_url( $img->getAttribute('src') );
156
- array_push( $results, $src );
157
  $srcset = $img->getAttribute('srcset');
158
  if ( !empty( $srcset ) ) {
159
  $setImgs = explode( ',', trim( $srcset ) );
@@ -169,17 +150,20 @@ class Meow_WPMC_Core {
169
  // Links, href
170
  $urls = $dom->getElementsByTagName( 'a' );
171
  foreach ( $urls as $url ) {
172
- $src = $this->wpmc_clean_url( $url->getAttribute('href') );
173
- if ( !empty( $src ) )
174
- array_push( $results, $src );
 
 
 
175
  }
176
 
177
  // PDF
178
  preg_match_all( "/((https?:\/\/)?[^\\&\#\[\] \"\?]+\.pdf)/", $html, $res );
179
  if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 ) {
180
  foreach ( $res[1] as $url ) {
181
- //error_log(print_r($url, 1));
182
- array_push( $results, $this->wpmc_clean_url( $url ) );
183
  }
184
  }
185
 
@@ -187,8 +171,8 @@ class Meow_WPMC_Core {
187
  preg_match_all( "/url\(\'?\"?((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png))\'?\"?/", $html, $res );
188
  if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 ) {
189
  foreach ( $res[1] as $url ) {
190
- //error_log(print_r($res, 1));
191
- array_push( $results, $this->wpmc_clean_url( $url ) );
192
  }
193
  }
194
 
@@ -206,7 +190,8 @@ class Meow_WPMC_Core {
206
  else if ( is_numeric( $value ) )
207
  array_push( $ids, $value );
208
  else
209
- array_push( $urls, $this->wpmc_clean_url( $value ) );
 
210
  }
211
  }
212
  }
@@ -639,16 +624,26 @@ class Meow_WPMC_Core {
639
  return $url;
640
  }
641
 
 
 
 
 
642
  function clean_url_from_resolution_ref( &$url ) {
643
  $url = $this->clean_url_from_resolution( $url );
644
  }
645
 
646
  // From a url to the shortened and cleaned url (for example '2013/02/file.png')
647
  function wpmc_clean_url( $url ) {
 
 
648
  $upload_folder = wp_upload_dir();
649
  $baseurl = $upload_folder['baseurl'];
650
- if ( substr( $url, 0, 2 ) === "//" )
651
  $url = "http:" . $url;
 
 
 
 
652
  $baseurl = str_replace( 'https://', 'http://', $baseurl );
653
  $baseurl = str_replace( 'http://www.', 'http://', $baseurl );
654
  $newurl = str_replace( 'https://', 'http://', $url );
@@ -656,9 +651,7 @@ class Meow_WPMC_Core {
656
  $newurl = str_replace( $baseurl, '', $newurl );
657
 
658
  // https://wordpress.org/support/topic/links-containing-encoded-special-characters/
659
- //$newurl = trim( $newurl, "/" );
660
  $newurl = urldecode( trim( $newurl, "/" ) );
661
-
662
  return $newurl;
663
  }
664
 
14
  $this->admin = $admin;
15
  if ( is_admin() )
16
  $this->admin_init();
17
+
18
+ // Initializers
19
+ add_action( 'wpmc_initialize_parsers', array( $this, 'initialize_parsers' ), 10, 0 );
20
+ }
21
+
22
+ function initialize_parsers() {
23
+ include_once( 'parsers.php' );
24
+ new MeowApps_WPMC_Parsers();
25
  }
26
 
27
  function admin_init() {
28
  $this->current_method = get_option( 'wpmc_method', 'media' );
29
  $types = "jpg|jpeg|jpe|gif|png|tiff|bmp|csv|pdf|xls|xlsx|doc|docx|tiff|mp3|mp4|wav|lua";
30
  $this->regex_file = str_replace( "MIMETYPES", $types, $this->regex_file );
 
 
 
 
31
  }
32
 
33
  function deepsleep( $seconds ) {
109
  }
110
  }
111
 
112
+ function get_favicon() {
113
+ $vals = get_option( 'wpseo_titles' );
114
+ $url = $vals['company_logo'];
115
+ if ( $this->is_url($url) )
116
+ return $this->wpmc_clean_url( $url );
117
+ return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  }
119
 
120
  function get_urls_from_html( $html ) {
122
  return array();
123
  libxml_use_internal_errors( false );
124
 
125
+ // Resolve src-set and shortcodes
126
+ $html = do_shortcode( $html );
127
+ $html = wp_make_content_images_responsive( $html );
128
+
129
  $dom = new DOMDocument();
130
+ @$dom->loadHTML( $html ); // mm change
131
 
132
  // Images, src, srcset
133
  $imgs = $dom->getElementsByTagName( 'img' );
134
  $results = array();
135
  foreach ( $imgs as $img ) {
136
  $src = $this->wpmc_clean_url( $img->getAttribute('src') );
137
+ array_push( $results, $src );
138
  $srcset = $img->getAttribute('srcset');
139
  if ( !empty( $srcset ) ) {
140
  $setImgs = explode( ',', trim( $srcset ) );
150
  // Links, href
151
  $urls = $dom->getElementsByTagName( 'a' );
152
  foreach ( $urls as $url ) {
153
+ $url_href = $url->getAttribute('href'); // mm change
154
+ if ( $this->is_url( $url_href ) ) { // mm change
155
+ $src = $this->wpmc_clean_url( $url_href ); // mm change
156
+ if ( !empty( $src ) )
157
+ array_push( $results, $src );
158
+ }
159
  }
160
 
161
  // PDF
162
  preg_match_all( "/((https?:\/\/)?[^\\&\#\[\] \"\?]+\.pdf)/", $html, $res );
163
  if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 ) {
164
  foreach ( $res[1] as $url ) {
165
+ if ( $this->is_url($url) )
166
+ array_push( $results, $this->wpmc_clean_url( $url ) );
167
  }
168
  }
169
 
171
  preg_match_all( "/url\(\'?\"?((https?:\/\/)?[^\\&\#\[\] \"\?]+\.(jpe?g|gif|png))\'?\"?/", $html, $res );
172
  if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 ) {
173
  foreach ( $res[1] as $url ) {
174
+ if ( $this->is_url($url) )
175
+ array_push( $results, $this->wpmc_clean_url( $url ) );
176
  }
177
  }
178
 
190
  else if ( is_numeric( $value ) )
191
  array_push( $ids, $value );
192
  else
193
+ if ( $this->is_url( $value ) )
194
+ array_push( $urls, $this->wpmc_clean_url( $value ) );
195
  }
196
  }
197
  }
624
  return $url;
625
  }
626
 
627
+ function is_url( $url ) {
628
+ return ( (!empty($url)) && is_string($url) && strlen($url)>4 && ( strtolower(substr($url,0,4))=='http' || $url[0]=='/' ) );
629
+ }
630
+
631
  function clean_url_from_resolution_ref( &$url ) {
632
  $url = $this->clean_url_from_resolution( $url );
633
  }
634
 
635
  // From a url to the shortened and cleaned url (for example '2013/02/file.png')
636
  function wpmc_clean_url( $url ) {
637
+ if ( empty( $url ) )
638
+ return $url;
639
  $upload_folder = wp_upload_dir();
640
  $baseurl = $upload_folder['baseurl'];
641
+ if ( substr( $url, 0, 2 ) === "//" ) {
642
  $url = "http:" . $url;
643
+ }
644
+ elseif ( $url[0]=="/" ) {
645
+ $url = get_site_url() . $url;
646
+ }
647
  $baseurl = str_replace( 'https://', 'http://', $baseurl );
648
  $baseurl = str_replace( 'http://www.', 'http://', $baseurl );
649
  $newurl = str_replace( 'https://', 'http://', $url );
651
  $newurl = str_replace( $baseurl, '', $newurl );
652
 
653
  // https://wordpress.org/support/topic/links-containing-encoded-special-characters/
 
654
  $newurl = urldecode( trim( $newurl, "/" ) );
 
655
  return $newurl;
656
  }
657
 
custom_checkers.php DELETED
@@ -1,20 +0,0 @@
1
- <?php
2
-
3
- // This file will contains all the CUSTOM checkers of the Media Cleaner system.
4
- // It will hook on the Media Cleaner filters to detect if certain media is in use by a plugin.
5
- // Each plugin or theme can have
6
-
7
- // WooCommerce
8
- // if ( is_plugin_active( 'woocommerce' ) ) ...
9
- //
10
-
11
- // WP/LR Sync
12
- // if ( is_plugin_active( 'wplr-sync' ) ) ...
13
- //
14
-
15
- // Justified Image Grid
16
- // Requested by: Alexander S. Kunz
17
- // Added by: Jordy Meow
18
- // Usage: Similar as WP internal gallery but use the justified_image_grid shortcode instead.
19
-
20
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
media-cleaner.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Media Cleaner
4
  Plugin URI: https://meowapps.com
5
  Description: Clean your Media Library, many options, trash system.
6
- Version: 5.1.3
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: media-cleaner
@@ -14,66 +14,67 @@ Originally developed for two of my websites:
14
  */
15
 
16
  if ( class_exists( 'Meow_WPMC_Core' ) ) {
17
- function wpmc_pro_admin_notices() {
18
- echo '<div class="error"><p>Thanks for installing the Pro version of Media Cleaner :) However, the free version is still enabled. Please disable or uninstall it.</p></div>';
19
- }
20
- add_action( 'admin_notices', 'wpmc_pro_admin_notices' );
21
- return;
22
  }
23
 
24
  if ( is_admin() ) {
25
 
26
- global $wpmc_version;
27
- $wpmc_version = '5.1.3';
28
 
29
- // Admin
30
- require __DIR__ . '/admin.php';
31
- $wpmc_admin = new Meow_WPMC_Admin( 'wpmc', __FILE__, 'media-cleaner' );
32
 
33
- // Core
34
- require __DIR__ . '/core.php';
35
- $wpmc_core = new Meow_WPMC_Core( $wpmc_admin );
36
- $wpmc_admin->core = $wpmc_core;
 
37
 
38
  // UI
39
  require __DIR__ . '/ui.php';
40
- new Meow_WPMC_UI( $wpmc_core, $wpmc_admin );
41
 
42
- /*******************************************************************************
43
- * TODO: OLD PRO, THIS FUNCTION SHOULD BE REMOVED IN THE FUTURE
44
- ******************************************************************************/
45
 
46
- add_action( 'admin_notices', 'wpmc_meow_old_version_admin_notices' );
47
 
48
- function wpmc_meow_old_version_admin_notices() {
49
- if ( isset( $_POST['wpmc_reset_sub'] ) ) {
50
- delete_transient( 'wpmc_validated' );
51
- delete_option( 'wpmc_pro_serial' );
52
- delete_option( 'wpmc_pro_status' );
53
- }
54
- $subscr_id = get_option( 'wpmc_pro_serial', "" );
55
- if ( empty( $subscr_id ) )
56
- return;
57
- $forever = strpos( $subscr_id, 'F-' ) !== false;
58
- $yearly = strpos( $subscr_id, 'I-' ) !== false;
59
- if ( !$forever && !$yearly )
60
- return;
61
- ?>
62
- <div class="error">
63
- <p>
64
- <h2>IMPORTANT MESSAGE ABOUT MEDIA CLEANER</h2>
65
- In order to comply with WordPress.org, BIG CHANGES in the code and how the plugin was sold were to be made. The plugin needs requires to be purchased and updated through the new <a target='_blank' href="https://store.meowapps.com">Meow Apps Store</a>. This store is also more robust (keys, websites management, invoices, etc). Now, since WordPress.org only accepts free plugins on its repository, this is the one currently installed. Therefore, you need to take an action. <b>Please click here to know more about your license and to learn what to do: <a target='_blank' href='https://meowapps.com/?mkey=<?php echo $subscr_id ?>'>License <?php echo $subscr_id ?></a></b>.
66
- </p>
67
- <p>
68
- <form method="post" action="">
69
- <input type="hidden" name="wpmc_reset_sub" value="true">
70
- <input type="submit" name="submit" id="submit" class="button" value="Got it. Clear this!">
71
- <br /><small><b>Make sure you followed the instruction before clicking this button.</b></small>
72
- </form>
73
- </p>
74
- </div>
75
- <?php
76
- }
77
 
78
  }
79
 
3
  Plugin Name: Media Cleaner
4
  Plugin URI: https://meowapps.com
5
  Description: Clean your Media Library, many options, trash system.
6
+ Version: 5.2.0
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: media-cleaner
14
  */
15
 
16
  if ( class_exists( 'Meow_WPMC_Core' ) ) {
17
+ function wpmc_pro_admin_notices() {
18
+ echo '<div class="error"><p>Thanks for installing the Pro version of Media Cleaner :) However, the free version is still enabled. Please disable or uninstall it.</p></div>';
19
+ }
20
+ add_action( 'admin_notices', 'wpmc_pro_admin_notices' );
21
+ return;
22
  }
23
 
24
  if ( is_admin() ) {
25
 
26
+ global $wpmc_version;
27
+ $wpmc_version = '5.2.0';
28
 
29
+ // Admin
30
+ require __DIR__ . '/admin.php';
31
+ $wpmc_admin = new Meow_WPMC_Admin( 'wpmc', __FILE__, 'media-cleaner' );
32
 
33
+ // Core
34
+ require __DIR__ . '/core.php';
35
+ global $wpmc;
36
+ $wpmc = new Meow_WPMC_Core( $wpmc_admin );
37
+ $wpmc_admin->core = $wpmc;
38
 
39
  // UI
40
  require __DIR__ . '/ui.php';
41
+ new Meow_WPMC_UI( $wpmc, $wpmc_admin );
42
 
43
+ /*******************************************************************************
44
+ * TODO: OLD PRO, THIS FUNCTION SHOULD BE REMOVED IN THE FUTURE
45
+ ******************************************************************************/
46
 
47
+ add_action( 'admin_notices', 'wpmc_meow_old_version_admin_notices' );
48
 
49
+ function wpmc_meow_old_version_admin_notices() {
50
+ if ( isset( $_POST['wpmc_reset_sub'] ) ) {
51
+ delete_transient( 'wpmc_validated' );
52
+ delete_option( 'wpmc_pro_serial' );
53
+ delete_option( 'wpmc_pro_status' );
54
+ }
55
+ $subscr_id = get_option( 'wpmc_pro_serial', "" );
56
+ if ( empty( $subscr_id ) )
57
+ return;
58
+ $forever = strpos( $subscr_id, 'F-' ) !== false;
59
+ $yearly = strpos( $subscr_id, 'I-' ) !== false;
60
+ if ( !$forever && !$yearly )
61
+ return;
62
+ ?>
63
+ <div class="error">
64
+ <p>
65
+ <h2>IMPORTANT MESSAGE ABOUT MEDIA CLEANER</h2>
66
+ In order to comply with WordPress.org, BIG CHANGES in the code and how the plugin was sold were to be made. The plugin needs requires to be purchased and updated through the new <a target='_blank' href="https://store.meowapps.com">Meow Apps Store</a>. This store is also more robust (keys, websites management, invoices, etc). Now, since WordPress.org only accepts free plugins on its repository, this is the one currently installed. Therefore, you need to take an action. <b>Please click here to know more about your license and to learn what to do: <a target='_blank' href='https://meowapps.com/?mkey=<?php echo $subscr_id ?>'>License <?php echo $subscr_id ?></a></b>.
67
+ </p>
68
+ <p>
69
+ <form method="post" action="">
70
+ <input type="hidden" name="wpmc_reset_sub" value="true">
71
+ <input type="submit" name="submit" id="submit" class="button" value="Got it. Clear this!">
72
+ <br /><small><b>Make sure you followed the instruction before clicking this button.</b></small>
73
+ </form>
74
+ </p>
75
+ </div>
76
+ <?php
77
+ }
78
 
79
  }
80
 
parsers.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MeowApps_WPMC_Parsers {
4
+
5
+ public function __construct() {
6
+ // require_once( ABSPATH . 'wp-admin/includes/plugin.php' ); // mm change
7
+ require_once( 'parsers/common.php' );
8
+ new MeowApps_WPMC_Parser();
9
+
10
+ if ( class_exists( 'WooCommerce' ) )
11
+ require_once( 'parsers/woocommerce.php' );
12
+
13
+ if ( class_exists( 'Attachments' ) ) // mm change
14
+ require_once( 'parsers/attachments.php' );
15
+
16
+ if ( class_exists( 'ACF' ) )
17
+ require_once( 'parsers/acf.php' );
18
+
19
+ if ( function_exists( 'acfw_globals' ) ) // mm change
20
+ require_once( 'parsers/acf_widgets.php' );
21
+
22
+ if ( class_exists( 'MetaSliderPlugin' ) || class_exists( 'MetaSliderPro' ) ) // mm change
23
+ require_once( 'parsers/metaslider.php' );
24
+ }
25
+ }
26
+
27
+ ?>
parsers/acf.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ add_action( 'wpmc_scan_postmeta', 'wpmc_scan_postmeta_acf' );
4
+
5
+ function wpmc_scan_postmeta_acf( $id ) {
6
+ $fields = get_field_objects( $id );
7
+ if ( is_array( $fields ) ) {
8
+ foreach ( $fields as $field )
9
+ wpmc_scan_postmeta_acf_field( $field, $id, 8 );
10
+ }
11
+ }
12
+
13
+ /**
14
+ * Scans a single ACF field object.
15
+ * If the specified field is a repeater or a flexible content,
16
+ * scans each subfield recursively.
17
+ *
18
+ * @param array $field
19
+ * An associative array replesenting a single ACF field.
20
+ * The actual array must be structured like this:
21
+ * array (
22
+ * 'name' => The name of the field
23
+ * 'type' => The field type i.e. 'text', 'object', 'repeater'
24
+ * 'value' => The value
25
+ * ...
26
+ * )
27
+ * @param int $id The post ID
28
+ * @param int $recursion_limit The max recursion depth. Negative number means unlimited
29
+ *
30
+ * @since ACF 5.6.10
31
+ */
32
+ function wpmc_scan_postmeta_acf_field( $field, $id, $recursion_limit = -1 ) {
33
+ if ( !isset( $field['type'] ) ) return;
34
+
35
+ global $wpmc;
36
+
37
+ /** Multiple Fields (Repeater or Flexible Content) **/
38
+ static $recursives = array ( // Possibly Recursive Types
39
+ 'repeater',
40
+ 'flexible_content'
41
+ );
42
+ if ( in_array( $field['type'], $recursives ) && have_rows( $field['name'], $id ) ) {
43
+ if ( $recursion_limit == 0 ) return; // Too much recursion
44
+ do { // Iterate over rows
45
+ $row = the_row( true );
46
+ foreach ( $row as $col => $value ) { // Iterate over columns (subfields)
47
+ $subfield = get_sub_field_object( $col, true, true );
48
+ if ( !is_array( $subfield ) ) continue;
49
+ if ( WP_DEBUG ) { // XXX Debug
50
+ if ( !isset( $subfield['value'] ) ) trigger_error( 'Unexpected Situation: $subfield[value] is not set', E_USER_ERROR );
51
+ if ( $subfield['value'] != $value ) trigger_error( 'Unexpected Situation: $subfield[value] has unexpected value', E_USER_ERROR );
52
+ }
53
+ wpmc_scan_postmeta_acf_field( $subfield, $id, $recursion_limit - 1 ); // Recursion
54
+ }
55
+ } while ( have_rows( $field['name'], $id ) );
56
+ return;
57
+ }
58
+ /** Singular Field **/
59
+ $postmeta_images_acf_ids = array();
60
+ $postmeta_images_acf_urls = array();
61
+
62
+ $format = "";
63
+ if ( isset( $field['return_format'] ) )
64
+ $format = $field['return_format'];
65
+ else if ( isset( $field['save_format'] ) )
66
+ $format = $field['save_format'];
67
+
68
+ // ACF Image ID and URL
69
+ if ( $field['type'] == 'image' && ( $format == 'array' || $format == 'object' ) ) {
70
+ if ( !empty( $field['value']['id'] ) )
71
+ array_push( $postmeta_images_acf_ids, $field['value']['id'] );
72
+ if ( !empty( $field['value']['url'] ) )
73
+ array_push( $postmeta_images_acf_urls, $wpmc->wpmc_clean_url( $field['value']['url'] ) );
74
+ }
75
+ // ACF Image ID
76
+ else if ( $field['type'] == 'image' && $format == 'id' && !empty( $field['value'] ) ) {
77
+ array_push( $postmeta_images_acf_ids, $field['value'] );
78
+ }
79
+ // ACF Image URL
80
+ else if ( $field['type'] == 'image' && $format == 'url' && !empty( $field['value'] ) ) {
81
+ array_push( $postmeta_images_acf_urls, $wpmc->wpmc_clean_url( $field['value'] ) );
82
+ }
83
+ // ACF Gallery
84
+ else if ( $field['type'] == 'gallery' && !empty( $field['value'] ) ) {
85
+ foreach ( $field['value'] as $media ) {
86
+ if ( !empty( $media['id'] ) )
87
+ array_push( $postmeta_images_acf_ids, $media['id'] );
88
+ }
89
+ }
90
+ $wpmc->add_reference_id( $postmeta_images_acf_ids, 'ACF (ID)' );
91
+ $wpmc->add_reference_url( $postmeta_images_acf_urls, 'ACF (URL)' );
92
+ }
93
+
94
+ ?>
parsers/acf_widgets.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // ACF Widget plugin
4
+ // https://acfwidgets.com
5
+ // Added by Mike Meinz
6
+
7
+ // Each field is a separate row in the wp_options table
8
+
9
+ add_action( 'wpmc_scan_widget', 'wpmc_scan_widget_acf_widgets', 10, 1 );
10
+
11
+ function wpmc_scan_widget_acf_widgets( $widget ) {
12
+ $acfwidget = $widget['callback'][0]->id;
13
+ If ( strlen($acfwidget)>11 && substr($acfwidget,0,11)=='acf_widget_' )
14
+ get_images_from_acfwidgets ( $acfwidget );
15
+ }
16
+
17
+ function get_images_from_acfwidgets( $widget) {
18
+ global $wpmc;
19
+ global $wpdb;
20
+ // $widget starts with: acf_widget_ and looks like this: acf_widget_15011-2
21
+ $LikeKey = 'widget_' . $widget . '_%'; // Example: option_name starts with widget_acf_widget_15216-3_
22
+ $q = "SELECT option_name, option_value FROM {$wpdb->options} where option_name like %s;";
23
+ $OptionRows = $wpdb->get_results( $wpdb->prepare( $q, $LikeKey ) , ARRAY_N );
24
+ if ( $wpdb->last_error ) {
25
+ error_log( $q . " " . $wpdb->last_error );
26
+ $this->log( $q . " " . $wpdb->last_error );
27
+ die( $wpdb->last_error );
28
+ }
29
+ if ( count( $OptionRows ) > 0 ) {
30
+ $ACFWidget_ids = array();
31
+ $ACFWidget_urls = array();
32
+ foreach( $OptionRows as $row ) {
33
+ //$row[0] = option_name from wp_options
34
+ //$row[1] = option_value from wp_options
35
+ // Three if statements in priority order (image ids, link fields, text fields)
36
+ // *** An image field containing a post id for the image or is it???
37
+ if ( strpos($row[0], 'image') || strpos($row[0], 'icon') !== false ) {
38
+ if ( is_numeric( $row[1] ) ) {
39
+ array_push( $ACFWidget_ids, $row[1] );
40
+ }
41
+ }
42
+
43
+ // No else here because sometimes image or icon is present in the option_name and link is also present
44
+ // Example: widget_acf_widget_15011-2_link_1_link_icon
45
+ // Example: widget_acf_widget_15216-3_widget_image_link
46
+
47
+ // *** A link field may contain a link or be empty
48
+ if ( strpos( $row[0], 'link' ) || strpos( $row[0], 'url' ) !== false ) {
49
+ if ( $wpmc->is_url($row[1]) ) {
50
+ $url = $wpmc->wpmc_clean_url($row[1]);
51
+ if (!empty($url)) {
52
+ array_push($ACFWidget_urls, $url);
53
+ }
54
+ }
55
+ }
56
+
57
+ // *** A text field may contain HTML
58
+ if (strpos($row[0], 'text') || strpos($row[0], 'html') !== false) {
59
+ if (!empty($row[1])) {
60
+ $ACFWidget_urls = array_merge($ACFWidget_urls, $wpmc->get_urls_from_html($row[1])); // mm change
61
+ }
62
+ }
63
+ }
64
+ if ( !empty( $ACFWidget_ids ) ) { // mm change
65
+ $wpmc->add_reference_id( $ACFWidget_ids , 'ACF WIDGET (ID)' );
66
+ }
67
+ if ( !empty( $ACFWidget_urls ) ) { // mm change
68
+ $wpmc->add_reference_url( $ACFWidget_urls , 'ACF WIDGET (URL)' );
69
+ }
70
+ }
71
+ }
72
+
73
+ ?>
parsers/attachments.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Attachment (https://wordpress.org/plugins/attachments/)
4
+ // Added by Mike Meinz
5
+ // Discussion: https://wordpress.org/support/topic/attachments-plugin/
6
+
7
+ add_action( 'wpmc_scan_postmeta', 'wpmc_scan_postmeta_attachments' );
8
+
9
+ function wpmc_scan_postmeta_attachments($id) {
10
+ global $wpmc;
11
+ $postmeta_images_ids = array();
12
+ $attachments_json = get_post_meta( $id, 'attachments', true ); // meta_key=='attachments'
13
+ $attachments_decoded = is_string( $attachments_json ) ? json_decode( $attachments_json ) : false;
14
+ if ( !empty( $attachments_decoded )) {
15
+ foreach ( $attachments_decoded as $AttachmentData => $TheAttachment ) {
16
+ foreach( $TheAttachment as $AttachmentData => $attachment ) {
17
+ array_push( $postmeta_images_ids, $attachment->id );
18
+ }
19
+ }
20
+ }
21
+ if ( !empty( $postmeta_images_ids ) ) {
22
+ $wpmc->add_reference_id( $postmeta_images_ids, 'ATTACHMENT (ID)' ); // mm change
23
+ }
24
+ }
25
+
26
+ ?>
parsers/common.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class MeowApps_WPMC_Parser {
4
+
5
+ private $metakeys = array( '%gallery%', '%ids%' );
6
+
7
+ public function __construct() {
8
+
9
+ // Check theme and favicon
10
+ add_action( 'wpmc_scan_once', array( $this, 'scan_once' ), 10, 0 );
11
+
12
+ // Check widgets for IDs and URLs
13
+ add_action( 'wpmc_scan_widget', array( $this, 'scan_widget' ), 10, 1 );
14
+
15
+ // Detect values in the general (known, based on %like%) Meta Keys
16
+ add_action( 'wpmc_scan_postmeta', array( $this, 'scan_postmeta' ), 10, 1 );
17
+
18
+ // Check URLs, IDs, WP Gallery
19
+ add_action( 'wpmc_scan_post', array( $this, 'scan_post' ), 10, 2 );
20
+ }
21
+
22
+ public function scan_once() {
23
+ global $wpmc;
24
+ $theme_ids = array();
25
+ $theme_urls = array();
26
+ $wpmc->get_images_from_themes( $theme_ids, $theme_urls );
27
+ $wpmc->add_reference_id( $theme_ids, 'THEME' );
28
+ $wpmc->add_reference_url( $theme_urls, 'THEME' );
29
+ $favicon = $wpmc->get_favicon();
30
+ if ( !empty( $favicon ) ) {
31
+ $wpmc->add_reference_url( $favicon, 'SITE ICON' );
32
+ }
33
+ }
34
+
35
+ function get_images_from_widget( $widget, &$ids, &$urls ) {
36
+ global $wpmc;
37
+ $widget_class = $widget['callback'][0]->option_name;
38
+ $instance_id = $widget['params'][0]['number'];
39
+ $widget_data = get_option( $widget_class );
40
+ if ( !empty( $widget_data[$instance_id]['text'] ) ) {
41
+ $html = $widget_data[$instance_id]['text']; // mm change
42
+ $urls = array_merge( $urls, $wpmc->get_urls_from_html( $html ) );
43
+ }
44
+ if ( !empty( $widget_data[$instance_id]['attachment_id'] ) ) {
45
+ $id = $widget_data[$instance_id]['attachment_id'];
46
+ array_push( $ids, $id );
47
+ }
48
+ if ( !empty( $widget_data[$instance_id]['url'] ) ) {
49
+ $url = $widget_data[$instance_id]['url'];
50
+ if ( $wpmc->is_url( $url ) ) {
51
+ $url = $wpmc->wpmc_clean_url( $url );
52
+ if ( !empty($url) )
53
+ array_push( $urls, $url );
54
+ }
55
+ }
56
+ if ( !empty( $widget_data[$instance_id]['ids'] ) ) {
57
+ $newIds = $widget_data[$instance_id]['ids'];
58
+ $ids = array_merge( $ids, $newIds );
59
+ }
60
+ }
61
+
62
+ public function scan_widget( $widget ) {
63
+ global $wpmc;
64
+ $widgets_ids = array();
65
+ $widgets_urls = array();
66
+ $this->get_images_from_widget( $widget, $widgets_ids, $widgets_urls );
67
+ $wpmc->add_reference_id( $widgets_ids, 'WIDGET' );
68
+ $wpmc->add_reference_url( $widgets_urls, 'WIDGET' );
69
+ }
70
+
71
+ public function scan_post( $html, $id ) {
72
+ global $wpmc;
73
+ $posts_images_urls = array();
74
+ $posts_images_ids = array();
75
+ $galleries_images = array();
76
+
77
+ // Check URLs in HTML
78
+ $new_urls = $wpmc->get_urls_from_html( $html );
79
+ $posts_images_urls = array_merge( $posts_images_urls, $new_urls );
80
+
81
+ // Check URLs in the Excerpt
82
+ $excerpt = get_post_field( 'post_excerpt', $id );
83
+ if ( !empty( $excerpt ) ) {
84
+ $new_urls = $wpmc->get_urls_from_html( $excerpt );
85
+ $posts_images_urls = array_merge( $posts_images_urls, $new_urls );
86
+ }
87
+
88
+ // Check for images IDs through classes in in posts
89
+ preg_match_all( "/wp-image-([0-9]+)/", $html, $res );
90
+ if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
91
+ $posts_images_ids = array_merge( $posts_images_ids, $res[1] );
92
+
93
+ // Standard WP Gallery
94
+ $galleries = get_post_galleries_images( $id );
95
+ foreach ( $galleries as $gallery ) {
96
+ foreach ( $gallery as $image ) {
97
+ array_push( $galleries_images, $wpmc->wpmc_clean_url( $image ) );
98
+ }
99
+ }
100
+
101
+ $wpmc->add_reference_id( $posts_images_ids, 'CONTENT (ID)' );
102
+ $wpmc->add_reference_url( $posts_images_urls, 'CONTENT (URL)' );
103
+ $wpmc->add_reference_url( $galleries_images, 'GALLERY (URL)' );
104
+ }
105
+
106
+ public function scan_postmeta( $id ) {
107
+ global $wpdb, $wpmc;
108
+
109
+ $likes = array ();
110
+ foreach ( $this->metakeys as $metakey ) $likes[] = "OR meta_key LIKE '{$metakey}'";
111
+ $likes = implode( ' ', $likes );
112
+
113
+ $q = <<< SQL
114
+ SELECT meta_value FROM {$wpdb->postmeta}
115
+ WHERE post_id = %d
116
+ AND (meta_key = '_thumbnail_id' {$likes})
117
+ SQL;
118
+ $metas = $wpdb->get_col( $wpdb->prepare( $q, $id ) );
119
+ if ( count( $metas ) > 0 ) {
120
+ $postmeta_images_ids = array();
121
+ $postmeta_images_urls = array();
122
+ foreach ( $metas as $meta ) {
123
+ // Just a number, let's assume it's a Media ID
124
+ if ( is_numeric( $meta ) ) {
125
+ if ( $meta > 0 )
126
+ array_push( $postmeta_images_ids, $meta );
127
+ continue;
128
+ }
129
+ $decoded = @unserialize( $meta );
130
+ if ( is_array( $decoded ) ) {
131
+ $wpmc->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
132
+ continue;
133
+ }
134
+ $exploded = explode( ',', $meta );
135
+ if ( is_array( $exploded ) ) {
136
+ $wpmc->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
137
+ continue;
138
+ }
139
+ }
140
+ $wpmc->add_reference_id( $postmeta_images_ids, 'META (ID)' );
141
+ $wpmc->add_reference_id( $postmeta_images_urls, 'META (URL)' );
142
+ }
143
+ }
144
+ }
parsers/metaslider.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ add_action( 'wpmc_scan_widgets', 'wpmc_scan_widgets_metaslider' );
4
+
5
+ function wpmc_scan_widgets_metaslider() {
6
+ global $wpmc;
7
+ $widgets_ids = array();
8
+ wpmc_get_images_from_metaslider( $widgets_ids );
9
+ $wpmc->add_reference_id( $widgets_ids, 'METASLIDER (ID)' ); // mm change
10
+ }
11
+
12
+ function wpmc_get_images_from_metaslider( &$ids ) {
13
+ global $wpdb;
14
+ $q = "SELECT object_id
15
+ FROM {$wpdb->term_relationships}
16
+ WHERE object_id > 0
17
+ AND term_taxonomy_id
18
+ IN (SELECT term_taxonomy_id FROM wp_term_taxonomy WHERE taxonomy = 'ml-slider');";
19
+ $imageIds = $wpdb->get_col( $q );
20
+ if ( $wpdb->last_error ) {
21
+ error_log( $q . " " . $wpdb->last_error );
22
+ $this->log( $q . " " . $wpdb->last_error );
23
+ die( $wpdb->last_error );
24
+ }
25
+ if ( count( $imageIds) > 0 ) {
26
+ $ids = array_merge( $ids, $imageIds );
27
+ }
28
+ }
29
+
30
+ ?>
parsers/woocommerce.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ add_action( 'wpmc_scan_once', 'wpmc_scan_once_woocommerce' );
4
+ add_action( 'wpmc_scan_postmeta', 'wpmc_scan_postmeta_woocommerce' );
5
+
6
+ // Only on Start: Analyze WooCommerce Categories Images
7
+ function wpmc_scan_once_woocommerce() {
8
+ global $wpdb, $wpmc;
9
+ $query = "SELECT meta_value
10
+ FROM $wpdb->termmeta
11
+ WHERE meta_key LIKE '%thumbnail_id%'";
12
+ $metas = $wpdb->get_col( $query );
13
+ if ( count( $metas ) > 0 ) {
14
+ $postmeta_images_ids = array();
15
+ foreach ( $metas as $meta )
16
+ if ( is_numeric( $meta ) && $meta > 0 )
17
+ array_push( $postmeta_images_ids, $meta );
18
+ $wpmc->add_reference_id( $postmeta_images_ids, 'META (ID)' );
19
+ }
20
+ }
21
+
22
+ function wpmc_scan_postmeta_woocommerce( $id ) {
23
+ global $wpdb, $wpmc;
24
+ $galleries_images_wc = array();
25
+ $res = $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE post_id = $id
26
+ AND meta_key = '_product_image_gallery'" );
27
+ foreach ( $res as $values ) {
28
+ $ids = explode( ',', $values );
29
+ $galleries_images_wc = array_merge( $galleries_images_wc, $ids );
30
+ }
31
+ $wpmc->add_reference_id( $galleries_images_wc, 'WOOCOOMMERCE (ID)' );
32
+ }
33
+
34
+ ?>
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: TigrouMeow
3
  Tags: clean, delete, file, files, images, image, media, library, upload, acf
4
  Requires at least: 4.8
5
- Requires PHP: 7.0
6
  Tested up to: 5.1
7
- Stable tag: 5.1.3
 
8
 
9
  Clean your WordPress (broken media, unused media, files). It has its own trash and recovery features. Please read the description.
10
 
@@ -52,6 +52,13 @@ Again, this plugin deletes files so be careful! Backup is not only important, it
52
 
53
  == Changelog ==
54
 
 
 
 
 
 
 
 
55
  = 5.1.3 =
56
  * Add: Support for WebP.
57
  * Update: Avoid removing tables when plugin is only disabled.
2
  Contributors: TigrouMeow
3
  Tags: clean, delete, file, files, images, image, media, library, upload, acf
4
  Requires at least: 4.8
 
5
  Tested up to: 5.1
6
+ Requires PHP: 7.0
7
+ Stable tag: 5.2.0
8
 
9
  Clean your WordPress (broken media, unused media, files). It has its own trash and recovery features. Please read the description.
10
 
52
 
53
  == Changelog ==
54
 
55
+ = 5.2.0 =
56
+ * Update: Many optimizations, modules and big sections of the code are now only loaded when really needed.
57
+ * Fix: Filenames with spaces weren't detected correctly and other.
58
+ * Fix: Make sure that the shortcodes are resolved.
59
+ * Add: Compatibility with more plugins (ACF Widgets, Attachments, Metaslider).
60
+ * Info: Mike Meinz is an amazing developer who made a thorough debugging of the whole process, made a lot of corrections, and added support for more plugins. Thanks to him, Media Cleaner is much better!
61
+
62
  = 5.1.3 =
63
  * Add: Support for WebP.
64
  * Update: Avoid removing tables when plugin is only disabled.
scan.php DELETED
@@ -1,200 +0,0 @@
1
- <?php
2
-
3
- class MeowApps_WPMC_Scan {
4
-
5
- private $core = null;
6
- private $metakeys = array( '%gallery%', '%ids%' );
7
-
8
- public function __construct( $core ) {
9
- $this->core = $core;
10
-
11
- // Detect values in the general (known, based on %like%) Meta Keys
12
- add_action( 'wpmc_scan_postmeta', array( $this, 'scan_postmeta' ) );
13
-
14
- if ( class_exists( 'WooCommerce' ) )
15
- add_action( 'wpmc_scan_postmeta', array( $this, 'scan_postmeta_woocommerce' ) );
16
-
17
- // Check URLs, IDs, WP Gallery, WooCommerce
18
- add_action( 'wpmc_scan_post', array( $this, 'scan_post' ), 10, 2 );
19
-
20
- // Advanced Custom Fields
21
- if ( class_exists( 'acf' ) )
22
- add_action( 'wpmc_scan_postmeta', array( $this, 'scan_postmeta_acf' ) );
23
-
24
- }
25
-
26
- public function scan_post( $html, $id ) {
27
- $posts_images_urls = array();
28
- $posts_images_ids = array();
29
- $galleries_images = array();
30
-
31
- // Check URLs in HTML
32
- $new_urls = $this->core->get_urls_from_html( $html );
33
- $posts_images_urls = array_merge( $posts_images_urls, $new_urls );
34
-
35
- // Check Excerpt for WooCommerce (= Product Short Description)
36
- if ( class_exists( 'WooCommerce' ) ) {
37
- $excerpt = get_post_field( 'post_excerpt', $id );
38
- if ( !empty( $excerpt ) ) {
39
- $new_urls = $this->core->get_urls_from_html( $excerpt );
40
- $posts_images_urls = array_merge( $posts_images_urls, $new_urls );
41
- }
42
- }
43
-
44
- // Check for images IDs through classes in in posts
45
- preg_match_all( "/wp-image-([0-9]+)/", $html, $res );
46
- if ( !empty( $res ) && isset( $res[1] ) && count( $res[1] ) > 0 )
47
- $posts_images_ids = array_merge( $posts_images_ids, $res[1] );
48
-
49
-
50
- // Standard WP Gallery
51
- $galleries = get_post_galleries_images( $id );
52
- foreach ( $galleries as $gallery ) {
53
- foreach ( $gallery as $image ) {
54
- array_push( $galleries_images, $this->core->wpmc_clean_url( $image ) );
55
- }
56
- }
57
-
58
- $this->core->add_reference_id( $posts_images_ids, 'CONTENT (ID)' );
59
- $this->core->add_reference_url( $posts_images_urls, 'CONTENT (URL)' );
60
- $this->core->add_reference_url( $galleries_images, 'GALLERY (URL)' );
61
- }
62
-
63
- public function scan_postmeta( $id ) {
64
- global $wpdb;
65
-
66
- $likes = array ();
67
- foreach ( $this->metakeys as $metakey ) $likes[] = "OR meta_key LIKE '{$metakey}'";
68
- $likes = implode( ' ', $likes );
69
-
70
- $q = <<< SQL
71
- SELECT meta_value FROM {$wpdb->postmeta}
72
- WHERE post_id = %d
73
- AND (meta_key = '_thumbnail_id' {$likes})
74
- SQL;
75
- $metas = $wpdb->get_col( $wpdb->prepare( $q, $id ) );
76
- if ( count( $metas ) > 0 ) {
77
- $postmeta_images_ids = array();
78
- $postmeta_images_urls = array();
79
- foreach ( $metas as $meta ) {
80
- // Just a number, let's assume it's a Media ID
81
- if ( is_numeric( $meta ) ) {
82
- if ( $meta > 0 )
83
- array_push( $postmeta_images_ids, $meta );
84
- continue;
85
- }
86
- $decoded = @unserialize( $meta );
87
- if ( is_array( $decoded ) ) {
88
- $this->core->array_to_ids_or_urls( $decoded, $postmeta_images_ids, $postmeta_images_urls );
89
- continue;
90
- }
91
- $exploded = explode( ',', $meta );
92
- if ( is_array( $exploded ) ) {
93
- $this->core->array_to_ids_or_urls( $exploded, $postmeta_images_ids, $postmeta_images_urls );
94
- continue;
95
- }
96
- }
97
- $this->core->add_reference_id( $postmeta_images_ids, 'META (ID)' );
98
- $this->core->add_reference_id( $postmeta_images_urls, 'META (URL)' );
99
- }
100
- }
101
-
102
- function scan_postmeta_woocommerce( $id ) {
103
- global $wpdb;
104
- $galleries_images_wc = array();
105
- $res = $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE post_id = $id
106
- AND meta_key = '_product_image_gallery'" );
107
- foreach ( $res as $values ) {
108
- $ids = explode( ',', $values );
109
- $galleries_images_wc = array_merge( $galleries_images_wc, $ids );
110
- }
111
- $this->core->add_reference_id( $galleries_images_wc, 'WOOCOOMMERCE (ID)' );
112
- }
113
-
114
- public function scan_postmeta_acf( $id ) {
115
- $fields = get_field_objects( $id );
116
- if ( is_array( $fields ) ) {
117
- foreach ( $fields as $field )
118
- $this->scan_postmeta_acf_field( $field, $id, 8 );
119
- }
120
- }
121
-
122
- /**
123
- * Scans a single ACF field object.
124
- * If the specified field is a repeater or a flexible content,
125
- * scans each subfield recursively.
126
- *
127
- * @param array $field
128
- * An associative array replesenting a single ACF field.
129
- * The actual array must be structured like this:
130
- * array (
131
- * 'name' => The name of the field
132
- * 'type' => The field type i.e. 'text', 'object', 'repeater'
133
- * 'value' => The value
134
- * ...
135
- * )
136
- * @param int $id The post ID
137
- * @param int $recursion_limit The max recursion depth. Negative number means unlimited
138
- *
139
- * @since ACF 5.6.10
140
- */
141
- public function scan_postmeta_acf_field( $field, $id, $recursion_limit = -1 ) {
142
- if ( !isset( $field['type'] ) ) return;
143
-
144
- /** Multiple Fields (Repeater or Flexible Content) **/
145
- static $recursives = array ( // Possibly Recursive Types
146
- 'repeater',
147
- 'flexible_content'
148
- );
149
- if ( in_array( $field['type'], $recursives ) && have_rows( $field['name'], $id ) ) {
150
- if ( $recursion_limit == 0 ) return; // Too much recursion
151
- do { // Iterate over rows
152
- $row = the_row( true );
153
- foreach ( $row as $col => $value ) { // Iterate over columns (subfields)
154
- $subfield = get_sub_field_object( $col, true, true );
155
- if ( !is_array( $subfield ) ) continue;
156
- if ( WP_DEBUG ) { // XXX Debug
157
- if ( !isset( $subfield['value'] ) ) trigger_error( 'Unexpected Situation: $subfield[value] is not set', E_USER_ERROR );
158
- if ( $subfield['value'] != $value ) trigger_error( 'Unexpected Situation: $subfield[value] has unexpected value', E_USER_ERROR );
159
- }
160
- $this->scan_postmeta_acf_field( $subfield, $id, $recursion_limit - 1 ); // Recursion
161
- }
162
- } while ( have_rows( $field['name'], $id ) );
163
- return;
164
- }
165
- /** Singular Field **/
166
- $postmeta_images_acf_ids = array();
167
- $postmeta_images_acf_urls = array();
168
-
169
- $format = "";
170
- if ( isset( $field['return_format'] ) )
171
- $format = $field['return_format'];
172
- else if ( isset( $field['save_format'] ) )
173
- $format = $field['save_format'];
174
-
175
- // ACF Image ID and URL
176
- if ( $field['type'] == 'image' && ( $format == 'array' || $format == 'object' ) ) {
177
- if ( !empty( $field['value']['id'] ) )
178
- array_push( $postmeta_images_acf_ids, $field['value']['id'] );
179
- if ( !empty( $field['value']['url'] ) )
180
- array_push( $postmeta_images_acf_urls, $this->core->wpmc_clean_url( $field['value']['url'] ) );
181
- }
182
- // ACF Image ID
183
- else if ( $field['type'] == 'image' && $format == 'id' && !empty( $field['value'] ) ) {
184
- array_push( $postmeta_images_acf_ids, $field['value'] );
185
- }
186
- // ACF Image URL
187
- else if ( $field['type'] == 'image' && $format == 'url' && !empty( $field['value'] ) ) {
188
- array_push( $postmeta_images_acf_urls, $this->core->wpmc_clean_url( $field['value'] ) );
189
- }
190
- // ACF Gallery
191
- else if ( $field['type'] == 'gallery' && !empty( $field['value'] ) ) {
192
- foreach ( $field['value'] as $media ) {
193
- if ( !empty( $media['id'] ) )
194
- array_push( $postmeta_images_acf_ids, $media['id'] );
195
- }
196
- }
197
- $this->core->add_reference_id( $postmeta_images_acf_ids, 'ACF (ID)' );
198
- $this->core->add_reference_url( $postmeta_images_acf_urls, 'ACF (URL)' );
199
- }
200
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ui.php CHANGED
@@ -1,498 +1,510 @@
1
- <?php
2
-
3
- class Meow_WPMC_UI {
4
- private $core = null;
5
- private $admin = null;
6
-
7
- function __construct( $core, $admin ) {
8
- $this->core = $core;
9
- $this->admin = $admin;
10
- add_action( 'admin_menu', array( $this, 'admin_menu' ) );
11
- add_action( 'admin_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
12
- add_action( 'admin_print_scripts', array( $this, 'admin_inline_js' ) );
13
- add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
14
- add_action( 'wp_ajax_wpmc_prepare_do', array( $this, 'wp_ajax_wpmc_prepare_do' ) );
15
- add_action( 'wp_ajax_wpmc_scan', array( $this, 'wp_ajax_wpmc_scan' ) );
16
- add_action( 'wp_ajax_wpmc_scan_do', array( $this, 'wp_ajax_wpmc_scan_do' ) );
17
- add_action( 'wp_ajax_wpmc_get_all_issues', array( $this, 'wp_ajax_wpmc_get_all_issues' ) );
18
- add_action( 'wp_ajax_wpmc_get_all_deleted', array( $this, 'wp_ajax_wpmc_get_all_deleted' ) );
19
- add_action( 'wp_ajax_wpmc_delete_do', array( $this, 'wp_ajax_wpmc_delete_do' ) );
20
- add_action( 'wp_ajax_wpmc_ignore_do', array( $this, 'wp_ajax_wpmc_ignore_do' ) );
21
- add_action( 'wp_ajax_wpmc_recover_do', array( $this, 'wp_ajax_wpmc_recover_do' ) );
22
- add_action( 'wp_ajax_wpmc_validate_option', array( $this, 'wp_ajax_wpmc_validate_option' ) );
23
- add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 10, 2 );
24
- }
25
-
26
- /**
27
- * Renders a view within the views directory.
28
- * @param string $view The name of the view to render
29
- * @param array $data
30
- * An associative array of variables to bind to the view.
31
- * Each key turns into a variable name.
32
- * @return string Rendered view
33
- */
34
- function render_view( $view, $data = null ) {
35
- ob_start();
36
- if ( is_array( $data ) ) extract( $data );
37
- include( __DIR__ . "/views/$view.php" );
38
- return ob_get_clean();
39
- }
40
-
41
- function admin_menu() {
42
- //load_plugin_textdomain( 'media-cleaner', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
43
- add_media_page( 'Media Cleaner', 'Cleaner', 'manage_options', 'media-cleaner', array( $this, 'wpmc_screen' ) );
44
- }
45
-
46
- function wpmc_screen() {
47
- global $wpdb, $wplr;
48
- echo $this->render_view( 'menu-screen', array(
49
- 'wpdb' => $wpdb,
50
- 'wplr' => $wplr,
51
- 'ui' => $this,
52
- 'core' => $this->core,
53
- 'admin' => $this->admin
54
- ) );
55
- }
56
-
57
- function wp_enqueue_scripts() {
58
- wp_enqueue_style( 'wp-jquery-ui-dialog' );
59
- wp_enqueue_script( 'jquery-ui-dialog' );
60
- wp_enqueue_style( 'media-cleaner-css', plugins_url( '/media-cleaner.css', __FILE__ ) );
61
-
62
- $screen = get_current_screen();
63
- global $wpmc_version;
64
- switch ( $screen->id ) {
65
- case 'media_page_media-cleaner': // Media > Cleaner
66
- wp_enqueue_script( 'media-cleaner', plugins_url( '/media-cleaner.js', __FILE__ ), array( 'jquery', 'jquery-ui-dialog' ),
67
- $wpmc_version, true );
68
- break;
69
- case 'meow-apps_page_wpmc_settings-menu': // Meow Apps > Media Cleaner (Settings)
70
- wp_enqueue_script( 'media-cleaner-settings', plugins_url( '/settings.js', __FILE__ ), array( 'jquery' ),
71
- $wpmc_version, true );
72
- break;
73
- }
74
- }
75
-
76
- /**
77
- *
78
- * DASHBOARD
79
- *
80
- */
81
-
82
- function admin_inline_js() {
83
- echo "<script type='text/javascript'>\n";
84
- echo 'var wpmc_cfg = {
85
- timeout: ' . ( (int) $this->core->get_max_execution_time() ) * 1000 . ',
86
- delay: ' . get_option( 'wpmc_delay', 100 ) . ',
87
- postsBuffer:' . get_option( 'wpmc_posts_buffer', 5 ) . ',
88
- mediasBuffer:' . get_option( 'wpmc_medias_buffer', 100 ) . ',
89
- analysisBuffer: ' . get_option( 'wpmc_analysis_buffer', 50 ) . ',
90
- isPro: ' . ( $this->admin->is_registered() ? '1' : '0') . ',
91
- scanFiles: ' . ( ( get_option( 'wpmc_method', 'media' ) == 'files' && $this->admin->is_registered() ) ? '1' : '0' ) . ',
92
- scanMedia: ' . ( get_option( 'wpmc_method', 'media' ) == 'media' ? '1' : '0' ) . ' };';
93
- echo "\n</script>";
94
- }
95
-
96
- /*******************************************************************************
97
- * METABOX FOR USAGE
98
- ******************************************************************************/
99
-
100
- function add_metabox() {
101
- add_meta_box( 'mfrh_media_usage_box', 'Media Cleaner', array( $this, 'display_metabox' ), 'attachment', 'side', 'default' );
102
- }
103
-
104
- function display_metabox( $post ) {
105
- $this->core->log( "Media Edit > Checking Media #{$post->ID}" );
106
- $success = $this->core->wpmc_check_media( $post->ID, true );
107
- $this->core->log( "Success $success\n" );
108
- if ( $success ) {
109
- if ( $this->core->last_analysis == "CONTENT" ) {
110
- echo "Found in content.";
111
- }
112
- else if ( $this->core->last_analysis == "CONTENT (ID)" ) {
113
- echo "Found in content (as an ID).";
114
- }
115
- else if ( $this->core->last_analysis == "CONTENT (URL)" ) {
116
- echo "Found in content (as an URL).";
117
- }
118
- else if ( $this->core->last_analysis == "THEME" ) {
119
- echo "Found in theme.";
120
- }
121
- else if ( $this->core->last_analysis == "PAGE BUILDER" ) {
122
- echo "Found in Page Builder.";
123
- }
124
- else if ( $this->core->last_analysis == "GALLERY" ) {
125
- echo "Found in gallery.";
126
- }
127
- else if ( $this->core->last_analysis == "META" ) {
128
- echo "Found in meta.";
129
- }
130
- else if ( $this->core->last_analysis == "META (ID)" ) {
131
- echo "Found in meta (as an ID).";
132
- }
133
- else if ( $this->core->last_analysis == "META (URL)" ) {
134
- echo "Found in meta (as an URL).";
135
- }
136
- else if ( $this->core->last_analysis == "META ACF (ID)" ) {
137
- echo "Found in ACF meta (as an ID).";
138
- }
139
- else if ( $this->core->last_analysis == "META ACF (URL)" ) {
140
- echo "Found in ACF meta (as an URL).";
141
- }
142
- else if ( $this->core->last_analysis == "WIDGET" ) {
143
- echo "Found in widget.";
144
- }
145
- else {
146
- echo "It seems to be used as: " . $this->core->last_analysis;
147
- }
148
- }
149
- else {
150
- echo "Doesn't seem to be used.";
151
- }
152
- }
153
-
154
- function media_row_actions( $actions, $post ) {
155
- global $current_screen;
156
- if ( 'upload' != $current_screen->id )
157
- return $actions;
158
- global $wpdb;
159
- $table_name = $wpdb->prefix . "mclean_scan";
160
- $res = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE postId = %d", $post->ID ) );
161
- if ( !empty( $res ) && isset( $actions['delete'] ) )
162
- $actions['delete'] = "<a href='?page=media-cleaner&view=deleted'>" .
163
- __( 'Delete with Media Cleaner', 'media-cleaner' ) . "</a>";
164
- if ( !empty( $res ) && isset( $actions['trash'] ) )
165
- $actions['trash'] = "<a href='?page=media-cleaner'>" .
166
- __( 'Trash with Media Cleaner', 'media-cleaner' ) . "</a>";
167
- if ( !empty( $res ) && isset( $actions['untrash'] ) ) {
168
- $actions['untrash'] = "<a href='?page=media-cleaner&view=deleted'>" .
169
- __( 'Restore with Media Cleaner', 'media-cleaner' ) . "</a>";
170
- }
171
- return $actions;
172
- }
173
-
174
- /*******************************************************************************
175
- * ASYNCHRONOUS AJAX FUNCTIONS
176
- ******************************************************************************/
177
-
178
- function wp_ajax_wpmc_prepare_do() {
179
- $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
180
- $limitsize = get_option( 'wpmc_posts_buffer', 5 );
181
- if ( empty( $limit ) )
182
- $this->core->wpmc_reset_issues();
183
-
184
- $method = get_option( 'wpmc_method', 'media' );
185
- $check_library = get_option(' wpmc_media_library', true );
186
- $check_postmeta = get_option( 'wpmc_postmeta', false );
187
- $check_posts = get_option( 'wpmc_posts', false );
188
- $check_widgets = get_option( 'wpmc_widgets', false );
189
- if ( $method == 'media' && !$check_posts && !$check_postmeta && !$check_widgets ) {
190
- echo json_encode( array(
191
- 'results' => array(),
192
- 'success' => true,
193
- 'finished' => true,
194
- 'message' => __( "Posts, Meta and Widgets analysis are all off. Done.", 'media-cleaner' )
195
- ) );
196
- die();
197
- }
198
- if ( $method == 'files' && $check_library && !$check_posts && !$check_postmeta && !$check_widgets ) {
199
- echo json_encode( array(
200
- 'results' => array(),
201
- 'success' => true,
202
- 'finished' => true,
203
- 'message' => __( "Posts, Meta and Widgets analysis are all off. Done.", 'media-cleaner' )
204
- ) );
205
- die();
206
- }
207
-
208
- global $wpdb;
209
- // Maybe we could avoid to check more post_types.
210
- // SELECT post_type, COUNT(*) FROM `wp_posts` GROUP BY post_type
211
- $posts = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
212
- WHERE p.post_status != 'inherit'
213
- AND p.post_status != 'trash'
214
- AND p.post_type != 'attachment'
215
- AND p.post_type != 'shop_order'
216
- AND p.post_type != 'shop_order_refund'
217
- AND p.post_type != 'nav_menu_item'
218
- AND p.post_type != 'revision'
219
- AND p.post_type != 'auto-draft'
220
- AND p.post_type != 'wphb_minify_group'
221
- AND p.post_type != 'customize_changeset'
222
- AND p.post_type != 'oembed_cache'
223
- AND p.post_type NOT LIKE '%acf-%'
224
- AND p.post_type NOT LIKE '%edd_%'
225
- LIMIT %d, %d", $limit, $limitsize
226
- )
227
- );
228
-
229
- $found = array();
230
-
231
- // Only at the beginning
232
- if ( empty( $limit ) ) {
233
-
234
- $this->core->log( "Analyzing for references:" );
235
-
236
- $theme_ids = array();
237
- $theme_urls = array();
238
- $this->core->get_images_from_themes( $theme_ids, $theme_urls );
239
- $this->core->add_reference_id( $theme_ids, 'THEME' );
240
- $this->core->add_reference_url( $theme_urls, 'THEME' );
241
-
242
- // Only on Start: Analyze Widgets
243
- if ( get_option( 'wpmc_widgets', false ) ) {
244
- $widgets_ids = array();
245
- $widgets_urls = array();
246
- $this->core->get_images_from_widgets( $widgets_ids, $widgets_urls );
247
- $this->core->add_reference_id( $widgets_ids, 'WIDGET' );
248
- $this->core->add_reference_url( $widgets_urls, 'WIDGET' );
249
- }
250
-
251
- // Only on Start: Analyze WooCommerce Categories Images
252
- if ( class_exists( 'WooCommerce' ) ) {
253
- $metas = $wpdb->get_col( "SELECT meta_value
254
- FROM $wpdb->termmeta
255
- WHERE meta_key LIKE '%thumbnail_id%'"
256
- );
257
- if ( count( $metas ) > 0 ) {
258
- $postmeta_images_ids = array();
259
- foreach ( $metas as $meta )
260
- if ( is_numeric( $meta ) && $meta > 0 )
261
- array_push( $postmeta_images_ids, $meta );
262
- $this->core->add_reference_id( $postmeta_images_ids, 'META (ID)' );
263
- }
264
- }
265
- }
266
-
267
- $this->core->timeout_check_start( count( $posts ) );
268
-
269
- foreach ( $posts as $post ) {
270
- $this->core->timeout_check();
271
- // Run the scanners
272
- if ( $check_postmeta )
273
- do_action( 'wpmc_scan_postmeta', $post );
274
- if ( $check_posts ) {
275
- // Get HTML for this post
276
- $html = get_post_field( 'post_content', $post );
277
- // Scan on the raw TML content
278
- do_action( 'wpmc_scan_post', $html, $post );
279
- $html = do_shortcode( $html );
280
- $html = wp_make_content_images_responsive( $html );
281
- // Scan with shortcodes resolved and src-set
282
- do_action( 'wpmc_scan_post', $html, $post );
283
- }
284
- $this->core->timeout_check_additem();
285
- }
286
- // Write the references cached by the scanners
287
- $this->core->write_references();
288
-
289
- $finished = count( $posts ) < $limitsize;
290
- if ( $finished ) {
291
- $found = array();
292
- // Optimize DB (but that takes too long!)
293
- //$table_name = $wpdb->prefix . "mclean_refs";
294
- // $wpdb->query ("DELETE a FROM $table_name as a, $table_name as b
295
- // WHERE (a.mediaId = b.mediaId OR a.mediaId IS NULL AND b.mediaId IS NULL)
296
- // AND (a.mediaUrl = b.mediaUrl OR a.mediaUrl IS NULL AND b.mediaUrl IS NULL)
297
- // AND (a.originType = b.originType OR a.originType IS NULL AND b.originType IS NULL)
298
- // AND (a.origin = b.origin OR a.origin IS NULL AND b.origin IS NULL)
299
- // AND a.ID < b.ID;" );
300
- // $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;" );
301
- // $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;" );
302
- }
303
- if ( $finished && get_option( 'wpmc_debuglogs', false ) ) {
304
- //$this->core->log( print_r( $found, true ) );
305
- }
306
- echo json_encode(
307
- array(
308
- 'success' => true,
309
- 'finished' => $finished,
310
- 'limit' => $limit + $limitsize,
311
- 'message' => __( "Posts checked.", 'media-cleaner' ) )
312
- );
313
- die();
314
- }
315
-
316
- function wp_ajax_wpmc_scan() {
317
- global $wpdb;
318
-
319
- $method = get_option( 'wpmc_method', 'media' );
320
- if ( !$this->admin->is_registered() )
321
- $method = 'media';
322
- $path = isset( $_POST['path'] ) ? $_POST['path'] : null;
323
- $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
324
- $limitsize = get_option( 'wpmc_medias_buffer', 100 );
325
-
326
- if ( $method == 'files' ) {
327
- $output = apply_filters( 'wpmc_list_uploaded_files', array(
328
- 'results' => array(), 'success' => false, 'message' => __( "Unavailable.", 'media-cleaner' )
329
- ), $path );
330
- echo json_encode( $output );
331
- die();
332
- }
333
-
334
- if ( $method == 'media' ) {
335
- // Prevent double scanning by removing filesystem entries that we have DB entries for
336
- $results = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
337
- WHERE p.post_status = 'inherit'
338
- AND p.post_type = 'attachment'
339
- LIMIT %d, %d", $limit, $limitsize
340
- )
341
- );
342
- $finished = count( $results ) < $limitsize;
343
- echo json_encode(
344
- array(
345
- 'results' => $results,
346
- 'success' => true,
347
- 'finished' => $finished,
348
- 'limit' => $limit + $limitsize,
349
- 'message' => __( "Medias retrieved.", 'media-cleaner' ) )
350
- );
351
- die();
352
- }
353
-
354
- // No task.
355
- echo json_encode( array( 'success' => false, 'message' => __( "No task.", 'media-cleaner' ) ) );
356
- die();
357
- }
358
-
359
- function wp_ajax_wpmc_scan_do() {
360
- // For debug, to pretend there is a timeout
361
- //$this->core->deepsleep(10);
362
- //header("HTTP/1.0 408 Request Timeout");
363
- //exit;
364
-
365
- ob_start();
366
- $type = $_POST['type'];
367
- $data = $_POST['data'];
368
- $this->core->timeout_check_start( count( $data ) );
369
- $success = 0;
370
- foreach ( $data as $piece ) {
371
- $this->core->timeout_check();
372
- if ( $type == 'file' ) {
373
- $this->core->log( "Check File: {$piece}" );
374
- $result = ( apply_filters( 'wpmc_check_file', true, $piece ) ? 1 : 0 );
375
- $this->core->log( "Success " . $result . "\n" );
376
- $success += $result;
377
- }
378
- else if ( $type == 'media' ) {
379
- $this->core->log( "Checking Media #{$piece}" );
380
- $result = ( $this->core->wpmc_check_media( $piece ) ? 1 : 0 );
381
- $this->core->log( "Success " . $result . "\n" );
382
- $success += $result;
383
- }
384
- $this->core->timeout_check_additem();
385
- }
386
- ob_end_clean();
387
- echo json_encode(
388
- array(
389
- 'success' => true,
390
- 'result' => array( 'type' => $type, 'data' => $data, 'success' => $success ),
391
- 'message' => __( "Items checked.", 'media-cleaner' )
392
- )
393
- );
394
- die();
395
- }
396
-
397
- function wp_ajax_wpmc_get_all_issues() {
398
- global $wpdb;
399
- $isTrash = ( isset( $_POST['isTrash'] ) && $_POST['isTrash'] == 1 ) ? true : false;
400
- $table_name = $wpdb->prefix . "mclean_scan";
401
- $q = "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = " . ( $isTrash ? 1 : 0 );
402
- if ( $search = ( isset( $_POST['s'] ) && $_POST['s'] ) ? sanitize_text_field( $_POST['s'] ) : '' )
403
- $q = $wpdb->prepare( $q . ' AND path LIKE %s', '%' . $wpdb->esc_like( $search ) . '%' );
404
- $ids = $wpdb->get_col( $q );
405
-
406
- echo json_encode(
407
- array(
408
- 'results' => array( 'ids' => $ids ),
409
- 'success' => true,
410
- 'message' => __( "List generated.", 'media-cleaner' )
411
- )
412
- );
413
- die;
414
- }
415
-
416
- function wp_ajax_wpmc_get_all_deleted() {
417
- global $wpdb;
418
- $table_name = $wpdb->prefix . "mclean_scan";
419
- $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 1" );
420
- echo json_encode(
421
- array(
422
- 'results' => array( 'ids' => $ids ),
423
- 'success' => true,
424
- 'message' => __( "List generated.", 'media-cleaner' )
425
- )
426
- );
427
- die;
428
- }
429
-
430
- function wp_ajax_wpmc_delete_do() {
431
- ob_start();
432
- $data = $_POST['data'];
433
- $success = 0;
434
- foreach ( $data as $piece ) {
435
- $success += ( $this->core->wpmc_delete( $piece ) ? 1 : 0 );
436
- }
437
- ob_end_clean();
438
- echo json_encode(
439
- array(
440
- 'success' => true,
441
- 'result' => array( 'data' => $data, 'success' => $success ),
442
- 'message' => __( "Status unknown.", 'media-cleaner' )
443
- )
444
- );
445
- die();
446
- }
447
-
448
- function wp_ajax_wpmc_ignore_do() {
449
- ob_start();
450
- $data = $_POST['data'];
451
- $success = 0;
452
- foreach ( $data as $piece ) {
453
- $success += ( $this->core->wpmc_ignore( $piece ) ? 1 : 0 );
454
- }
455
- ob_end_clean();
456
- echo json_encode(
457
- array(
458
- 'success' => true,
459
- 'result' => array( 'data' => $data, 'success' => $success ),
460
- 'message' => __( "Status unknown.", 'media-cleaner' )
461
- )
462
- );
463
- die();
464
- }
465
-
466
- function wp_ajax_wpmc_recover_do() {
467
- ob_start();
468
- $data = $_POST['data'];
469
- $success = 0;
470
- foreach ( $data as $piece ) {
471
- $success += ( $this->core->wpmc_recover( $piece ) ? 1 : 0 );
472
- }
473
- ob_end_clean();
474
- echo json_encode(
475
- array(
476
- 'success' => true,
477
- 'result' => array( 'data' => $data, 'success' => $success ),
478
- 'message' => __( "Status unknown.", 'media-cleaner' )
479
- )
480
- );
481
- die();
482
- }
483
-
484
- function wp_ajax_wpmc_validate_option() {
485
- $name = $_POST['name']; // Option Name
486
- $value = $_POST['value']; // Option Value
487
- $value = wp_unslash( $value ); // Unescape backslashes
488
- $validated = $this->admin->validate_option( $name, $value );
489
- if ( $validated instanceof WP_Error ) { // Invalid value
490
- $error = array (
491
- 'code' => $validated->get_error_code() ?: 'invalid_option',
492
- 'message' => $validated->get_error_message() ?: __( "Invalid Option Value", 'media-cleaner' )
493
- );
494
- wp_send_json_error( $error );
495
- }
496
- wp_send_json_success();
497
- }
498
- }
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Meow_WPMC_UI {
4
+ private $core = null;
5
+ private $admin = null;
6
+
7
+ function __construct( $core, $admin ) {
8
+ $this->core = $core;
9
+ $this->admin = $admin;
10
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
11
+ add_action( 'admin_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
12
+ add_action( 'admin_print_scripts', array( $this, 'admin_inline_js' ) );
13
+ add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
14
+ add_action( 'wp_ajax_wpmc_prepare_do', array( $this, 'wp_ajax_wpmc_prepare_do' ) );
15
+ add_action( 'wp_ajax_wpmc_scan', array( $this, 'wp_ajax_wpmc_scan' ) );
16
+ add_action( 'wp_ajax_wpmc_scan_do', array( $this, 'wp_ajax_wpmc_scan_do' ) );
17
+ add_action( 'wp_ajax_wpmc_get_all_issues', array( $this, 'wp_ajax_wpmc_get_all_issues' ) );
18
+ add_action( 'wp_ajax_wpmc_get_all_deleted', array( $this, 'wp_ajax_wpmc_get_all_deleted' ) );
19
+ add_action( 'wp_ajax_wpmc_delete_do', array( $this, 'wp_ajax_wpmc_delete_do' ) );
20
+ add_action( 'wp_ajax_wpmc_ignore_do', array( $this, 'wp_ajax_wpmc_ignore_do' ) );
21
+ add_action( 'wp_ajax_wpmc_recover_do', array( $this, 'wp_ajax_wpmc_recover_do' ) );
22
+ add_action( 'wp_ajax_wpmc_validate_option', array( $this, 'wp_ajax_wpmc_validate_option' ) );
23
+ add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 10, 2 );
24
+ }
25
+
26
+ /**
27
+ * Renders a view within the views directory.
28
+ * @param string $view The name of the view to render
29
+ * @param array $data
30
+ * An associative array of variables to bind to the view.
31
+ * Each key turns into a variable name.
32
+ * @return string Rendered view
33
+ */
34
+ function render_view( $view, $data = null ) {
35
+ ob_start();
36
+ if ( is_array( $data ) ) extract( $data );
37
+ include( __DIR__ . "/views/$view.php" );
38
+ return ob_get_clean();
39
+ }
40
+
41
+ function admin_menu() {
42
+ //load_plugin_textdomain( 'media-cleaner', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
43
+ add_media_page( 'Media Cleaner', 'Cleaner', 'manage_options', 'media-cleaner', array( $this, 'wpmc_screen' ) );
44
+ }
45
+
46
+ function wpmc_screen() {
47
+ global $wpdb, $wplr;
48
+ echo $this->render_view( 'menu-screen', array(
49
+ 'wpdb' => $wpdb,
50
+ 'wplr' => $wplr,
51
+ 'ui' => $this,
52
+ 'core' => $this->core,
53
+ 'admin' => $this->admin
54
+ ) );
55
+ }
56
+
57
+ function wp_enqueue_scripts() {
58
+ wp_enqueue_style( 'wp-jquery-ui-dialog' );
59
+ wp_enqueue_script( 'jquery-ui-dialog' );
60
+ wp_enqueue_style( 'media-cleaner-css', plugins_url( '/media-cleaner.css', __FILE__ ) );
61
+
62
+ $screen = get_current_screen();
63
+ global $wpmc_version;
64
+ switch ( $screen->id ) {
65
+ case 'media_page_media-cleaner': // Media > Cleaner
66
+ wp_enqueue_script( 'media-cleaner', plugins_url( '/media-cleaner.js', __FILE__ ), array( 'jquery', 'jquery-ui-dialog' ),
67
+ $wpmc_version, true );
68
+ break;
69
+ case 'meow-apps_page_wpmc_settings-menu': // Meow Apps > Media Cleaner (Settings)
70
+ wp_enqueue_script( 'media-cleaner-settings', plugins_url( '/settings.js', __FILE__ ), array( 'jquery' ),
71
+ $wpmc_version, true );
72
+ break;
73
+ }
74
+ }
75
+
76
+ /**
77
+ *
78
+ * DASHBOARD
79
+ *
80
+ */
81
+
82
+ function admin_inline_js() {
83
+ echo "<script type='text/javascript'>\n";
84
+ echo 'var wpmc_cfg = {
85
+ timeout: ' . ( (int) $this->core->get_max_execution_time() ) * 1000 . ',
86
+ delay: ' . get_option( 'wpmc_delay', 100 ) . ',
87
+ postsBuffer:' . get_option( 'wpmc_posts_buffer', 5 ) . ',
88
+ mediasBuffer:' . get_option( 'wpmc_medias_buffer', 100 ) . ',
89
+ analysisBuffer: ' . get_option( 'wpmc_analysis_buffer', 50 ) . ',
90
+ isPro: ' . ( $this->admin->is_registered() ? '1' : '0') . ',
91
+ scanFiles: ' . ( ( get_option( 'wpmc_method', 'media' ) == 'files' && $this->admin->is_registered() ) ? '1' : '0' ) . ',
92
+ scanMedia: ' . ( get_option( 'wpmc_method', 'media' ) == 'media' ? '1' : '0' ) . ' };';
93
+ echo "\n</script>";
94
+ }
95
+
96
+ /*******************************************************************************
97
+ * METABOX FOR USAGE
98
+ ******************************************************************************/
99
+
100
+ function add_metabox() {
101
+ add_meta_box( 'mfrh_media_usage_box', 'Media Cleaner', array( $this, 'display_metabox' ), 'attachment', 'side', 'default' );
102
+ }
103
+
104
+ function display_metabox( $post ) {
105
+ $this->core->log( "Media Edit > Checking Media #{$post->ID}" );
106
+ $success = $this->core->wpmc_check_media( $post->ID, true );
107
+ $this->core->log( "Success $success\n" );
108
+ if ( $success ) {
109
+ if ( $this->core->last_analysis == "CONTENT" ) {
110
+ echo "Found in content.";
111
+ }
112
+ else if ( $this->core->last_analysis == "CONTENT (ID)" ) {
113
+ echo "Found in content (as an ID).";
114
+ }
115
+ else if ( $this->core->last_analysis == "CONTENT (URL)" ) {
116
+ echo "Found in content (as an URL).";
117
+ }
118
+ else if ( $this->core->last_analysis == "THEME" ) {
119
+ echo "Found in theme.";
120
+ }
121
+ else if ( $this->core->last_analysis == "PAGE BUILDER" ) {
122
+ echo "Found in Page Builder.";
123
+ }
124
+ else if ( $this->core->last_analysis == "GALLERY" ) {
125
+ echo "Found in gallery.";
126
+ }
127
+ else if ( $this->core->last_analysis == "META" ) {
128
+ echo "Found in meta.";
129
+ }
130
+ else if ( $this->core->last_analysis == "META (ID)" ) {
131
+ echo "Found in meta (as an ID).";
132
+ }
133
+ else if ( $this->core->last_analysis == "META (URL)" ) {
134
+ echo "Found in meta (as an URL).";
135
+ }
136
+ else if ( $this->core->last_analysis == "META ACF (ID)" ) {
137
+ echo "Found in ACF meta (as an ID).";
138
+ }
139
+ else if ( $this->core->last_analysis == "META ACF (URL)" ) {
140
+ echo "Found in ACF meta (as an URL).";
141
+ }
142
+ else if ( $this->core->last_analysis == "WIDGET" ) {
143
+ echo "Found in widget.";
144
+ }
145
+ else if ( $this->core->last_analysis == "ACF WIDGET (ID)" ) {
146
+ echo "Found in ACF Widget (as an ID).";
147
+ }
148
+ else if ( $this->core->last_analysis == "ACF WIDGET (URL)" ) {
149
+ echo "Found in ACF Widget (as an URL).";
150
+ }
151
+ else if ( $this->core->last_analysis == "ATTACHMENT (ID)" ) {
152
+ echo "Found in Attachment.";
153
+ }
154
+ else if ( $this->core->last_analysis == "METASLIDER (ID)" ) { // mm change
155
+ echo "Found in MetaSlider (as an ID)."; // mm change
156
+ }
157
+ else if ( $this->core->last_analysis == "SITE ICON" ) {
158
+ echo "Found in Site Icon.";
159
+ }
160
+ else {
161
+ echo "It seems to be used as: " . $this->core->last_analysis;
162
+ }
163
+ }
164
+ else {
165
+ echo "Doesn't seem to be used.";
166
+ }
167
+ }
168
+
169
+ function media_row_actions( $actions, $post ) {
170
+ global $current_screen;
171
+ if ( 'upload' != $current_screen->id )
172
+ return $actions;
173
+ global $wpdb;
174
+ $table_name = $wpdb->prefix . "mclean_scan";
175
+ $res = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE postId = %d", $post->ID ) );
176
+ if ( !empty( $res ) && isset( $actions['delete'] ) )
177
+ $actions['delete'] = "<a href='?page=media-cleaner&view=deleted'>" .
178
+ __( 'Delete with Media Cleaner', 'media-cleaner' ) . "</a>";
179
+ if ( !empty( $res ) && isset( $actions['trash'] ) )
180
+ $actions['trash'] = "<a href='?page=media-cleaner'>" .
181
+ __( 'Trash with Media Cleaner', 'media-cleaner' ) . "</a>";
182
+ if ( !empty( $res ) && isset( $actions['untrash'] ) ) {
183
+ $actions['untrash'] = "<a href='?page=media-cleaner&view=deleted'>" .
184
+ __( 'Restore with Media Cleaner', 'media-cleaner' ) . "</a>";
185
+ }
186
+ return $actions;
187
+ }
188
+
189
+ /*******************************************************************************
190
+ * ASYNCHRONOUS AJAX FUNCTIONS
191
+ ******************************************************************************/
192
+
193
+ function wp_ajax_wpmc_prepare_do() {
194
+ $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
195
+ $limitsize = get_option( 'wpmc_posts_buffer', 5 );
196
+ if ( empty( $limit ) )
197
+ $this->core->wpmc_reset_issues();
198
+
199
+ $method = get_option( 'wpmc_method', 'media' );
200
+ $check_library = get_option(' wpmc_media_library', true );
201
+ $check_postmeta = get_option( 'wpmc_postmeta', false );
202
+ $check_posts = get_option( 'wpmc_posts', false );
203
+ $check_widgets = get_option( 'wpmc_widgets', false );
204
+ if ( $method == 'media' && !$check_posts && !$check_postmeta && !$check_widgets ) {
205
+ echo json_encode( array(
206
+ 'results' => array(),
207
+ 'success' => true,
208
+ 'finished' => true,
209
+ 'message' => __( "Posts, Meta and Widgets analysis are all off. Done.", 'media-cleaner' )
210
+ ) );
211
+ die();
212
+ }
213
+ if ( $method == 'files' && $check_library && !$check_posts && !$check_postmeta && !$check_widgets ) {
214
+ echo json_encode( array(
215
+ 'results' => array(),
216
+ 'success' => true,
217
+ 'finished' => true,
218
+ 'message' => __( "Posts, Meta and Widgets analysis are all off. Done.", 'media-cleaner' )
219
+ ) );
220
+ die();
221
+ }
222
+
223
+ // Initialize the parsers
224
+ do_action( 'wpmc_initialize_parsers' );
225
+
226
+ global $wpdb;
227
+ // Maybe we could avoid to check more post_types.
228
+ // SELECT post_type, COUNT(*) FROM `wp_posts` GROUP BY post_type
229
+ $posts = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
230
+ WHERE p.post_status != 'inherit'
231
+ AND p.post_status != 'trash'
232
+ AND p.post_type != 'attachment'
233
+ AND p.post_type != 'shop_order'
234
+ AND p.post_type != 'shop_order_refund'
235
+ AND p.post_type != 'nav_menu_item'
236
+ AND p.post_type != 'revision'
237
+ AND p.post_type != 'auto-draft'
238
+ AND p.post_type != 'wphb_minify_group'
239
+ AND p.post_type != 'customize_changeset'
240
+ AND p.post_type != 'oembed_cache'
241
+ AND p.post_type NOT LIKE '%acf-%'
242
+ AND p.post_type NOT LIKE '%edd_%'
243
+ LIMIT %d, %d", $limit, $limitsize
244
+ )
245
+ );
246
+
247
+ $found = array();
248
+
249
+ // Only at the beginning
250
+ if ( empty( $limit ) ) {
251
+ $this->core->log( "Analyzing for references:" );
252
+ if ( get_option( 'wpmc_widgets', false ) ) {
253
+
254
+ global $wp_registered_widgets;
255
+ $syswidgets = $wp_registered_widgets;
256
+ $active_widgets = get_option( 'sidebars_widgets' );
257
+ foreach ( $active_widgets as $sidebar_name => $widgets ) {
258
+ if ( $sidebar_name != 'wp_inactive_widgets' && !empty( $widgets ) && is_array( $widgets ) ) {
259
+ foreach ( $widgets as $key => $widget ) {
260
+ do_action( 'wpmc_scan_widget', $syswidgets[$widget] );
261
+ }
262
+ }
263
+ }
264
+
265
+ do_action( 'wpmc_scan_widgets' );
266
+ }
267
+ do_action( 'wpmc_scan_once' );
268
+ }
269
+
270
+ $this->core->timeout_check_start( count( $posts ) );
271
+
272
+ foreach ( $posts as $post ) {
273
+ $this->core->timeout_check();
274
+ // Run the scanners
275
+ if ( $check_postmeta )
276
+ do_action( 'wpmc_scan_postmeta', $post );
277
+ if ( $check_posts ) {
278
+ // Get HTML for this post
279
+ $html = get_post_field( 'post_content', $post );
280
+
281
+ // Scan on the raw HTML content (useless?)
282
+ //do_action( 'wpmc_scan_post', $html, $post );
283
+
284
+ // This code was moved to the core.php (get_urls_from_html)
285
+ //$html = do_shortcode( $html );
286
+ //$html = wp_make_content_images_responsive( $html );
287
+ // Scan with shortcodes resolved and src-set
288
+
289
+ do_action( 'wpmc_scan_post', $html, $post );
290
+ }
291
+ $this->core->timeout_check_additem();
292
+ }
293
+
294
+ // Write the references cached by the scanners
295
+ $this->core->write_references();
296
+
297
+ $finished = count( $posts ) < $limitsize;
298
+ if ( $finished ) {
299
+ $found = array();
300
+ // Optimize DB (but that takes too long!)
301
+ //$table_name = $wpdb->prefix . "mclean_refs";
302
+ // $wpdb->query ("DELETE a FROM $table_name as a, $table_name as b
303
+ // WHERE (a.mediaId = b.mediaId OR a.mediaId IS NULL AND b.mediaId IS NULL)
304
+ // AND (a.mediaUrl = b.mediaUrl OR a.mediaUrl IS NULL AND b.mediaUrl IS NULL)
305
+ // AND (a.originType = b.originType OR a.originType IS NULL AND b.originType IS NULL)
306
+ // AND (a.origin = b.origin OR a.origin IS NULL AND b.origin IS NULL)
307
+ // AND a.ID < b.ID;" );
308
+ // $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;" );
309
+ // $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;" );
310
+ }
311
+ if ( $finished && get_option( 'wpmc_debuglogs', false ) ) {
312
+ //$this->core->log( print_r( $found, true ) );
313
+ }
314
+ echo json_encode(
315
+ array(
316
+ 'success' => true,
317
+ 'finished' => $finished,
318
+ 'limit' => $limit + $limitsize,
319
+ 'message' => __( "Posts checked.", 'media-cleaner' ) )
320
+ );
321
+ die();
322
+ }
323
+
324
+ function wp_ajax_wpmc_scan() {
325
+ global $wpdb;
326
+
327
+ $method = get_option( 'wpmc_method', 'media' );
328
+ if ( !$this->admin->is_registered() )
329
+ $method = 'media';
330
+ $path = isset( $_POST['path'] ) ? $_POST['path'] : null;
331
+ $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
332
+ $limitsize = get_option( 'wpmc_medias_buffer', 100 );
333
+
334
+ if ( $method == 'files' ) {
335
+ $output = apply_filters( 'wpmc_list_uploaded_files', array(
336
+ 'results' => array(), 'success' => false, 'message' => __( "Unavailable.", 'media-cleaner' )
337
+ ), $path );
338
+ echo json_encode( $output );
339
+ die();
340
+ }
341
+
342
+ if ( $method == 'media' ) {
343
+ // Prevent double scanning by removing filesystem entries that we have DB entries for
344
+ $results = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
345
+ WHERE p.post_status = 'inherit'
346
+ AND p.post_type = 'attachment'
347
+ LIMIT %d, %d", $limit, $limitsize
348
+ )
349
+ );
350
+ $finished = count( $results ) < $limitsize;
351
+ echo json_encode(
352
+ array(
353
+ 'results' => $results,
354
+ 'success' => true,
355
+ 'finished' => $finished,
356
+ 'limit' => $limit + $limitsize,
357
+ 'message' => __( "Medias retrieved.", 'media-cleaner' ) )
358
+ );
359
+ die();
360
+ }
361
+
362
+ // No task.
363
+ echo json_encode( array( 'success' => false, 'message' => __( "No task.", 'media-cleaner' ) ) );
364
+ die();
365
+ }
366
+
367
+ function wp_ajax_wpmc_scan_do() {
368
+ // For debug, to pretend there is a timeout
369
+ //$this->core->deepsleep(10);
370
+ //header("HTTP/1.0 408 Request Timeout");
371
+ //exit;
372
+
373
+ // Initialize the checkers
374
+ include_once( 'checkers.php' );
375
+ $this->core->checkers = new Meow_WPMC_Checkers( $this->core );
376
+
377
+ ob_start();
378
+ $type = $_POST['type'];
379
+ $data = $_POST['data'];
380
+ $this->core->timeout_check_start( count( $data ) );
381
+ $success = 0;
382
+ foreach ( $data as $piece ) {
383
+ $this->core->timeout_check();
384
+ if ( $type == 'file' ) {
385
+ $this->core->log( "\nCheck File: {$piece}" );
386
+ $result = ( apply_filters( 'wpmc_check_file', true, $piece ) ? 1 : 0 );
387
+ $this->core->log( "Success " . $result );
388
+ $success += $result;
389
+ }
390
+ else if ( $type == 'media' ) {
391
+ $this->core->log( "\nChecking Media #{$piece}" );
392
+ $result = ( $this->core->wpmc_check_media( $piece ) ? 1 : 0 );
393
+ $this->core->log( "Success " . $result );
394
+ $success += $result;
395
+ }
396
+ $this->core->timeout_check_additem();
397
+ }
398
+ ob_end_clean();
399
+ echo json_encode(
400
+ array(
401
+ 'success' => true,
402
+ 'result' => array( 'type' => $type, 'data' => $data, 'success' => $success ),
403
+ 'message' => __( "Items checked.", 'media-cleaner' )
404
+ )
405
+ );
406
+ die();
407
+ }
408
+
409
+ function wp_ajax_wpmc_get_all_issues() {
410
+ global $wpdb;
411
+ $isTrash = ( isset( $_POST['isTrash'] ) && $_POST['isTrash'] == 1 ) ? true : false;
412
+ $table_name = $wpdb->prefix . "mclean_scan";
413
+ $q = "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = " . ( $isTrash ? 1 : 0 );
414
+ if ( $search = ( isset( $_POST['s'] ) && $_POST['s'] ) ? sanitize_text_field( $_POST['s'] ) : '' )
415
+ $q = $wpdb->prepare( $q . ' AND path LIKE %s', '%' . $wpdb->esc_like( $search ) . '%' );
416
+ $ids = $wpdb->get_col( $q );
417
+
418
+ echo json_encode(
419
+ array(
420
+ 'results' => array( 'ids' => $ids ),
421
+ 'success' => true,
422
+ 'message' => __( "List generated.", 'media-cleaner' )
423
+ )
424
+ );
425
+ die;
426
+ }
427
+
428
+ function wp_ajax_wpmc_get_all_deleted() {
429
+ global $wpdb;
430
+ $table_name = $wpdb->prefix . "mclean_scan";
431
+ $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 1" );
432
+ echo json_encode(
433
+ array(
434
+ 'results' => array( 'ids' => $ids ),
435
+ 'success' => true,
436
+ 'message' => __( "List generated.", 'media-cleaner' )
437
+ )
438
+ );
439
+ die;
440
+ }
441
+
442
+ function wp_ajax_wpmc_delete_do() {
443
+ ob_start();
444
+ $data = $_POST['data'];
445
+ $success = 0;
446
+ foreach ( $data as $piece ) {
447
+ $success += ( $this->core->wpmc_delete( $piece ) ? 1 : 0 );
448
+ }
449
+ ob_end_clean();
450
+ echo json_encode(
451
+ array(
452
+ 'success' => true,
453
+ 'result' => array( 'data' => $data, 'success' => $success ),
454
+ 'message' => __( "Status unknown.", 'media-cleaner' )
455
+ )
456
+ );
457
+ die();
458
+ }
459
+
460
+ function wp_ajax_wpmc_ignore_do() {
461
+ ob_start();
462
+ $data = $_POST['data'];
463
+ $success = 0;
464
+ foreach ( $data as $piece ) {
465
+ $success += ( $this->core->wpmc_ignore( $piece ) ? 1 : 0 );
466
+ }
467
+ ob_end_clean();
468
+ echo json_encode(
469
+ array(
470
+ 'success' => true,
471
+ 'result' => array( 'data' => $data, 'success' => $success ),
472
+ 'message' => __( "Status unknown.", 'media-cleaner' )
473
+ )
474
+ );
475
+ die();
476
+ }
477
+
478
+ function wp_ajax_wpmc_recover_do() {
479
+ ob_start();
480
+ $data = $_POST['data'];
481
+ $success = 0;
482
+ foreach ( $data as $piece ) {
483
+ $success += ( $this->core->wpmc_recover( $piece ) ? 1 : 0 );
484
+ }
485
+ ob_end_clean();
486
+ echo json_encode(
487
+ array(
488
+ 'success' => true,
489
+ 'result' => array( 'data' => $data, 'success' => $success ),
490
+ 'message' => __( "Status unknown.", 'media-cleaner' )
491
+ )
492
+ );
493
+ die();
494
+ }
495
+
496
+ function wp_ajax_wpmc_validate_option() {
497
+ $name = $_POST['name']; // Option Name
498
+ $value = $_POST['value']; // Option Value
499
+ $value = wp_unslash( $value ); // Unescape backslashes
500
+ $validated = $this->admin->validate_option( $name, $value );
501
+ if ( $validated instanceof WP_Error ) { // Invalid value
502
+ $error = array (
503
+ 'code' => $validated->get_error_code() ?: 'invalid_option',
504
+ 'message' => $validated->get_error_message() ?: __( "Invalid Option Value", 'media-cleaner' )
505
+ );
506
+ wp_send_json_error( $error );
507
+ }
508
+ wp_send_json_success();
509
+ }
510
+ }