Media Cleaner - Version 5.6.2

Version Description

  • Add: Always Skip/Retry feature.
  • Add: "Images Only" for Media Library scan.
  • Add: Support for Salient theme.
  • Info: This plugin is a lot of work. Please help me by giving it a nice review, here.
Download this release

Release Info

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

Code changes from version 5.6.1 to 5.6.2

Files changed (8) hide show
  1. admin.php +13 -2
  2. api.php +40 -3
  3. engine.php +60 -26
  4. media-cleaner.php +2 -2
  5. readme.txt +8 -7
  6. scripts/dashboard.js +157 -30
  7. scripts/style.css +9 -0
  8. views/menu-screen.php +8 -0
admin.php CHANGED
@@ -104,6 +104,9 @@ class Meow_WPMC_Admin extends MeowApps_Admin {
104
 
105
  // SUBMENU > Settings > Filters
106
  add_settings_section( 'wpmc_filters_settings', null, null, 'wpmc_filters_settings-menu' );
 
 
 
107
  add_settings_field( 'wpmc_thumbnails_only', __( 'Thumbnails Only', 'media-cleaner' ),
108
  array( $this, 'admin_thumbnails_only_callback' ),
109
  'wpmc_filters_settings-menu', 'wpmc_filters_settings' );
@@ -161,6 +164,7 @@ class Meow_WPMC_Admin extends MeowApps_Admin {
161
  register_setting( 'wpmc_settings', 'wpmc_media_library' );
162
  register_setting( 'wpmc_settings', 'wpmc_debuglogs' );
163
 
 
164
  register_setting( 'wpmc_filters_settings', 'wpmc_thumbnails_only' );
165
  register_setting( 'wpmc_filters_settings', 'wpmc_dirs_filter' );
166
  register_setting( 'wpmc_filters_settings', 'wpmc_files_filter' );
@@ -413,12 +417,19 @@ HTML;
413
  echo $html;
414
  }
415
 
 
 
 
 
 
 
 
 
416
  function admin_thumbnails_only_callback( $args ) {
417
- $value = get_option( 'wpmc_thumbnails_only', false );
418
  $html = '<input type="checkbox" id="wpmc_thumbnails_only" name="wpmc_thumbnails_only" value="1" ' .
419
  disabled( get_option( 'wpmc_method', 'media' ) == 'files', false, false ) . ' ' .
420
  checked( 1, get_option( 'wpmc_thumbnails_only' ), false ) . '/>';
421
- $html .= '<label>' . __( 'Enable', 'media-cleaner' ) . '</label><br /><small>' . __( 'Restrict the filesystem scan to thumbnails (files containing the resolution). If none of the checks above are selected, you will get the list of all the thumbnails and be able to remove them.', 'media-cleaner' ) . '</small>';
422
  echo $html;
423
  }
424
 
104
 
105
  // SUBMENU > Settings > Filters
106
  add_settings_section( 'wpmc_filters_settings', null, null, 'wpmc_filters_settings-menu' );
107
+ add_settings_field( 'wpmc_images_only', __( 'Images Only', 'media-cleaner' ),
108
+ array( $this, 'admin_images_only_callback' ),
109
+ 'wpmc_filters_settings-menu', 'wpmc_filters_settings' );
110
  add_settings_field( 'wpmc_thumbnails_only', __( 'Thumbnails Only', 'media-cleaner' ),
111
  array( $this, 'admin_thumbnails_only_callback' ),
112
  'wpmc_filters_settings-menu', 'wpmc_filters_settings' );
164
  register_setting( 'wpmc_settings', 'wpmc_media_library' );
165
  register_setting( 'wpmc_settings', 'wpmc_debuglogs' );
166
 
167
+ register_setting( 'wpmc_filters_settings', 'wpmc_images_only' );
168
  register_setting( 'wpmc_filters_settings', 'wpmc_thumbnails_only' );
169
  register_setting( 'wpmc_filters_settings', 'wpmc_dirs_filter' );
170
  register_setting( 'wpmc_filters_settings', 'wpmc_files_filter' );
417
  echo $html;
418
  }
419
 
420
+ function admin_images_only_callback( $args ) {
421
+ $html = '<input type="checkbox" id="wpmc_images_only" name="wpmc_images_only" value="1" ' .
422
+ disabled( get_option( 'wpmc_method', 'media' ) == 'media', false, false ) . ' ' .
423
+ checked( 1, get_option( 'wpmc_images_only' ), false ) . '/>';
424
+ $html .= '<label>' . __( 'Enable', 'media-cleaner' ) . '</label><br /><small>' . __( 'Restrict the Media Library scan to images. Therefore, no documents or anything else will be scanned.', 'media-cleaner' ) . '</small>';
425
+ echo $html;
426
+ }
427
+
428
  function admin_thumbnails_only_callback( $args ) {
 
429
  $html = '<input type="checkbox" id="wpmc_thumbnails_only" name="wpmc_thumbnails_only" value="1" ' .
430
  disabled( get_option( 'wpmc_method', 'media' ) == 'files', false, false ) . ' ' .
431
  checked( 1, get_option( 'wpmc_thumbnails_only' ), false ) . '/>';
432
+ $html .= '<label>' . __( 'Enable', 'media-cleaner' ) . '</label><br /><small>' . __( 'Restrict the Filesystem scan to thumbnails (files containing the resolution). If none of the checks above are selected, you will get the list of all the images and be able to remove them.', 'media-cleaner' ) . '</small>';
433
  echo $html;
434
  }
435
 
api.php CHANGED
@@ -2,10 +2,16 @@
2
 
3
  class Meow_WPMC_API {
4
 
 
 
 
 
 
5
  function __construct( $core, $admin, $engine ) {
6
  $this->core = $core;
7
  $this->engine = $engine;
8
  $this->admin = $admin;
 
9
  add_action( 'wp_ajax_wpmc_extract_references', array( $this, 'wp_ajax_wpmc_extract_references' ) );
10
  add_action( 'wp_ajax_wpmc_retrieve_targets', array( $this, 'wp_ajax_wpmc_retrieve_targets' ) );
11
  add_action( 'wp_ajax_wpmc_check_targets', array( $this, 'wp_ajax_wpmc_check_targets' ) );
@@ -21,17 +27,48 @@ class Meow_WPMC_API {
21
  * ASYNCHRONOUS AJAX FUNCTIONS
22
  ******************************************************************************/
23
 
24
- // Anayze the posts to extract the references.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  function wp_ajax_wpmc_extract_references() {
26
  $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
27
  $source = isset( $_POST['source'] ) ? $_POST['source'] : null;
28
  $limitsize = get_option( 'wpmc_posts_buffer', 5 );
29
 
30
  $finished = false;
 
31
  if ( $source === 'content' )
32
- $finished = $this->engine->extractRefsFromContent( $limit, $limitsize, $message ); // $message is set by run()
33
  else if ( $source === 'media' )
34
- $finished = $this->engine->extractRefsFromLibrary( $limit, $limitsize, $message ); // $message is set by run()
35
  else {
36
  error_log('Media Cleaner: No source was mentioned while calling the extract_references action.');
37
  }
2
 
3
  class Meow_WPMC_API {
4
 
5
+ /**
6
+ * @param Meow_WPMC_Core $core
7
+ * @param Meow_WPMC_Admin $admin
8
+ * @param Meow_WPMC_Engine $engine
9
+ */
10
  function __construct( $core, $admin, $engine ) {
11
  $this->core = $core;
12
  $this->engine = $engine;
13
  $this->admin = $admin;
14
+ add_action( 'wp_ajax_wpmc_get_num_posts', array( $this, 'wp_ajax_wpmc_get_num_posts' ) );
15
  add_action( 'wp_ajax_wpmc_extract_references', array( $this, 'wp_ajax_wpmc_extract_references' ) );
16
  add_action( 'wp_ajax_wpmc_retrieve_targets', array( $this, 'wp_ajax_wpmc_retrieve_targets' ) );
17
  add_action( 'wp_ajax_wpmc_check_targets', array( $this, 'wp_ajax_wpmc_check_targets' ) );
27
  * ASYNCHRONOUS AJAX FUNCTIONS
28
  ******************************************************************************/
29
 
30
+ /**
31
+ * Method: POST
32
+ * Params:
33
+ * - source: 'content' | 'media'
34
+ * Return:
35
+ * - data.action: 'get_num_posts'
36
+ * - data.num: A number of the posts
37
+ */
38
+ function wp_ajax_wpmc_get_num_posts() {
39
+ $src = isset( $_POST['source'] ) ? $_POST['source'] : null;
40
+ $num = 0;
41
+
42
+ switch ($src) {
43
+ case 'content':
44
+ $num = count( $this->engine->get_posts_to_check() );
45
+ break;
46
+ case 'media':
47
+ $num = count( $this->engine->get_media_entries() );
48
+ break;
49
+ }
50
+
51
+ exit( json_encode( array (
52
+ 'success' => true,
53
+ 'data' => array (
54
+ 'action' => 'get_num_posts',
55
+ 'num' => $num
56
+ )
57
+ )));
58
+ }
59
+
60
+ // Analyze the posts to extract the references.
61
  function wp_ajax_wpmc_extract_references() {
62
  $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
63
  $source = isset( $_POST['source'] ) ? $_POST['source'] : null;
64
  $limitsize = get_option( 'wpmc_posts_buffer', 5 );
65
 
66
  $finished = false;
67
+ $message = ""; // will be filled by extractRefsFrom...
68
  if ( $source === 'content' )
69
+ $finished = $this->engine->extractRefsFromContent( $limit, $limitsize, $message );
70
  else if ( $source === 'media' )
71
+ $finished = $this->engine->extractRefsFromLibrary( $limit, $limitsize, $message );
72
  else {
73
  error_log('Media Cleaner: No source was mentioned while calling the extract_references action.');
74
  }
engine.php CHANGED
@@ -11,6 +11,37 @@ class Meow_WPMC_Engine {
11
  STEP 1: Parse the content, and look for references
12
  */
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  // Parse the posts for references (based on $limit and $limitsize for paging the scan)
15
  function extractRefsFromContent( $limit, $limitsize, &$message = '' ) {
16
  if ( empty( $limit ) )
@@ -36,18 +67,7 @@ class Meow_WPMC_Engine {
36
  do_action( 'wpmc_initialize_parsers' );
37
 
38
  global $wpdb;
39
- // Maybe we could avoid to check more post_types.
40
- // SELECT post_type, COUNT(*) FROM `wp_posts` GROUP BY post_type
41
- $posts = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
42
- WHERE p.post_status NOT IN ('inherit', 'trash', 'auto-draft')
43
- AND p.post_type NOT IN ('attachment', 'shop_order', 'shop_order_refund', 'nav_menu_item', 'revision', 'auto-draft', 'wphb_minify_group', 'customize_changeset', 'oembed_cache', 'nf_sub')
44
- AND p.post_type NOT LIKE 'dlssus_%'
45
- AND p.post_type NOT LIKE 'ml-slide%'
46
- AND p.post_type NOT LIKE '%acf-%'
47
- AND p.post_type NOT LIKE '%edd_%'
48
- LIMIT %d, %d", $limit, $limitsize
49
- )
50
- );
51
 
52
  // Only at the beginning
53
  if ( empty( $limit ) ) {
@@ -116,12 +136,7 @@ class Meow_WPMC_Engine {
116
  }
117
 
118
  global $wpdb;
119
- $medias = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
120
- WHERE p.post_status = 'inherit'
121
- AND p.post_type = 'attachment'
122
- LIMIT %d, %d", $limit, $limitsize
123
- )
124
- );
125
 
126
  // Only at the beginning
127
  if ( empty( $limit ) ) {
@@ -158,15 +173,34 @@ class Meow_WPMC_Engine {
158
  return $files;
159
  }
160
 
161
- function get_media_entries( $limit, $limitsize ) {
 
 
 
 
 
 
162
  global $wpdb;
163
- $results = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
164
- WHERE p.post_status = 'inherit'
165
- AND p.post_type = 'attachment'
166
- LIMIT %d, %d", $limit, $limitsize
167
- )
168
- );
169
- return $results;
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  }
171
 
172
  /*
11
  STEP 1: Parse the content, and look for references
12
  */
13
 
14
+ /**
15
+ * Returns the posts to check the references
16
+ * @param int $offset Negative number means no limit
17
+ * @param int $size Negative number means no limit
18
+ * @return NULL|array
19
+ */
20
+ function get_posts_to_check( $offset = -1, $size = -1 ) {
21
+ global $wpdb;
22
+ $r = null;
23
+
24
+ // Maybe we could avoid to check more post_types.
25
+ // SELECT post_type, COUNT(*) FROM `wp_posts` GROUP BY post_type
26
+ $q = <<<SQL
27
+ SELECT p.ID FROM $wpdb->posts p
28
+ WHERE p.post_status NOT IN ('inherit', 'trash', 'auto-draft')
29
+ AND p.post_type NOT IN ('attachment', 'shop_order', 'shop_order_refund', 'nav_menu_item', 'revision', 'auto-draft', 'wphb_minify_group', 'customize_changeset', 'oembed_cache', 'nf_sub')
30
+ AND p.post_type NOT LIKE 'dlssus_%'
31
+ AND p.post_type NOT LIKE 'ml-slide%'
32
+ AND p.post_type NOT LIKE '%acf-%'
33
+ AND p.post_type NOT LIKE '%edd_%'
34
+ SQL;
35
+ if ( $offset >= 0 && $size >= 0 ) {
36
+ $q .= " LIMIT %d, %d";
37
+ $r = $wpdb->get_col( $wpdb->prepare( $q, $offset, $size ) );
38
+
39
+ } else // No limit
40
+ $r = $wpdb->get_col( $q );
41
+
42
+ return $r;
43
+ }
44
+
45
  // Parse the posts for references (based on $limit and $limitsize for paging the scan)
46
  function extractRefsFromContent( $limit, $limitsize, &$message = '' ) {
47
  if ( empty( $limit ) )
67
  do_action( 'wpmc_initialize_parsers' );
68
 
69
  global $wpdb;
70
+ $posts = $this->get_posts_to_check( $limit, $limitsize );
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  // Only at the beginning
73
  if ( empty( $limit ) ) {
136
  }
137
 
138
  global $wpdb;
139
+ $medias = $this->get_media_entries( $limit, $limitsize );
 
 
 
 
 
140
 
141
  // Only at the beginning
142
  if ( empty( $limit ) ) {
173
  return $files;
174
  }
175
 
176
+ /**
177
+ * Returns the media entries to check the references
178
+ * @param int $offset Negative number means no limit
179
+ * @param int $size Negative number means no limit
180
+ * @return NULL|array
181
+ */
182
+ function get_media_entries( $offset = -1, $size = -1 ) {
183
  global $wpdb;
184
+ $r = null;
185
+
186
+ $q = <<<SQL
187
+ SELECT p.ID FROM $wpdb->posts p
188
+ WHERE p.post_status = 'inherit'
189
+ AND p.post_type = 'attachment'
190
+ SQL;
191
+ if ( get_option( 'wpmc_images_only' ) ) {
192
+ // Get only media entries which are images
193
+ $q .= " AND p.post_mime_type IN ( 'image/jpeg' )";
194
+ }
195
+
196
+ if ( $offset >= 0 && $size >= 0 ) {
197
+ $q .= " LIMIT %d, %d";
198
+ $r = $wpdb->get_col( $wpdb->prepare( $q, $offset, $size ) );
199
+
200
+ } else // No limit
201
+ $r = $wpdb->get_col( $q );
202
+
203
+ return $r;
204
  }
205
 
206
  /*
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.6.1
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: media-cleaner
@@ -25,7 +25,7 @@ if ( is_admin() ) {
25
 
26
  global $wpmc_version;
27
  global $wpmc;
28
- $wpmc_version = '5.6.1';
29
 
30
  require __DIR__ . '/admin.php';
31
  require __DIR__ . '/core.php';
3
  Plugin Name: Media Cleaner
4
  Plugin URI: https://meowapps.com
5
  Description: Clean your Media Library, many options, trash system.
6
+ Version: 5.6.2
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: media-cleaner
25
 
26
  global $wpmc_version;
27
  global $wpmc;
28
+ $wpmc_version = '5.6.2';
29
 
30
  require __DIR__ . '/admin.php';
31
  require __DIR__ . '/core.php';
readme.txt CHANGED
@@ -5,7 +5,7 @@ Donate link: https://commerce.coinbase.com/checkout/d047546a-77a8-41c8-9ea9-4a95
5
  Requires at least: 4.8
6
  Tested up to: 5.3
7
  Requires PHP: 7.0
8
- Stable tag: 5.6.1
9
 
10
  Clean your WordPress from unused or broken media and files. It has its own trash system and recovery features. Please read the description.
11
 
@@ -74,9 +74,14 @@ Better to be safe than sorry. This plugin deletes files! Therefore, backup is no
74
 
75
  == Changelog ==
76
 
 
 
 
 
 
 
77
  = 5.6.1 =
78
  * Add: You can now sort the results by size and path. Little present for the week-end ;)
79
- * Info: This plugin is a lot of work. Please help me by giving it a nice review, [here](https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post).
80
 
81
  = 5.5.8 =
82
  * Add: Support for Image Map Pro.
@@ -86,7 +91,6 @@ Better to be safe than sorry. This plugin deletes files! Therefore, backup is no
86
  * Fix: Check if the filename exists in the trash for every new upload (and if yes, give it a different filename).
87
  * Fix: Avoid crash related to unserialization.
88
  * Fix: Ignore some other plugins' files which are not supposed to be scanned.
89
- * Info: This plugin is a lot of work. Please help me by giving it a nice review, [here](https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post).
90
 
91
  = 5.5.7 =
92
  * Update: UI improved in many ways, I hope you will love it more!
@@ -115,7 +119,6 @@ Better to be safe than sorry. This plugin deletes files! Therefore, backup is no
115
  * Fix: Issue with the URLs pointing at the plugin's tutorial page.
116
  * Fix: Avoid the scan to be halted by error logging.
117
  * Add: Basic support for WCFM MarketPlace.
118
- * Info: This plugin is a lot of work. Please help by giving it a nice review, here: https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post. Thank you :)
119
 
120
  = 5.5.1 =
121
  * Update: Admin refreshed to 2.4.
@@ -262,7 +265,7 @@ Better to be safe than sorry. This plugin deletes files! Therefore, backup is no
262
  * Fix: Avoid checking the empty arrays.
263
 
264
  = 4.2.0 =
265
- * Info: This is a MAJOR UPDATE both in term of optimization and detection. Keep my motivation up and give a good review to the plugin here: https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post. That helps me a lot.
266
  * Add: Support for Fusion Builder (Avada).
267
  * Add: Cache the results found in posts to analyze them much faster later.
268
  * Add: Debugging log file (option).
@@ -297,14 +300,12 @@ Better to be safe than sorry. This plugin deletes files! Therefore, backup is no
297
 
298
  = 3.6.4 =
299
  * Fix: Plugin was not working properly with broken Media metadata. It now handles it properly.
300
- * Info: If you want to give me a bit of motivation, write a review on https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post.
301
 
302
  = 3.6.2 =
303
  * Fix: When over 1 GO, was displaying a lower size value.
304
  * Fix: Counting wasn't exact with a Filesystem scan.
305
  * Info: Please read the previous changelog as it didn't appear in WP for some reason.
306
  * Add: Check Posts also look for the Media ID in the classes (more secure).
307
- * Info: If you want to give me a bit of motivation, write a review on https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post.
308
 
309
  = 3.6.0 =
310
  * Add: Now the Media can be recovered! You can remove your Media through the plugin, make sure they are not in use (by testing your website thoroughly) and later delete them definitely from the trash. I think you will find it awesome.
5
  Requires at least: 4.8
6
  Tested up to: 5.3
7
  Requires PHP: 7.0
8
+ Stable tag: 5.6.2
9
 
10
  Clean your WordPress from unused or broken media and files. It has its own trash system and recovery features. Please read the description.
11
 
74
 
75
  == Changelog ==
76
 
77
+ = 5.6.2 =
78
+ * Add: Always Skip/Retry feature.
79
+ * Add: "Images Only" for Media Library scan.
80
+ * Add: Support for Salient theme.
81
+ * Info: This plugin is a lot of work. Please help me by giving it a nice review, [here](https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post).
82
+
83
  = 5.6.1 =
84
  * Add: You can now sort the results by size and path. Little present for the week-end ;)
 
85
 
86
  = 5.5.8 =
87
  * Add: Support for Image Map Pro.
91
  * Fix: Check if the filename exists in the trash for every new upload (and if yes, give it a different filename).
92
  * Fix: Avoid crash related to unserialization.
93
  * Fix: Ignore some other plugins' files which are not supposed to be scanned.
 
94
 
95
  = 5.5.7 =
96
  * Update: UI improved in many ways, I hope you will love it more!
119
  * Fix: Issue with the URLs pointing at the plugin's tutorial page.
120
  * Fix: Avoid the scan to be halted by error logging.
121
  * Add: Basic support for WCFM MarketPlace.
 
122
 
123
  = 5.5.1 =
124
  * Update: Admin refreshed to 2.4.
265
  * Fix: Avoid checking the empty arrays.
266
 
267
  = 4.2.0 =
268
+ * Info: This is a MAJOR UPDATE both in term of optimization and detection.
269
  * Add: Support for Fusion Builder (Avada).
270
  * Add: Cache the results found in posts to analyze them much faster later.
271
  * Add: Debugging log file (option).
300
 
301
  = 3.6.4 =
302
  * Fix: Plugin was not working properly with broken Media metadata. It now handles it properly.
 
303
 
304
  = 3.6.2 =
305
  * Fix: When over 1 GO, was displaying a lower size value.
306
  * Fix: Counting wasn't exact with a Filesystem scan.
307
  * Info: Please read the previous changelog as it didn't appear in WP for some reason.
308
  * Add: Check Posts also look for the Media ID in the classes (more secure).
 
309
 
310
  = 3.6.0 =
311
  * Add: Now the Media can be recovered! You can remove your Media through the plugin, make sure they are not in use (by testing your website thoroughly) and later delete them definitely from the trash. I think you will find it awesome.
scripts/dashboard.js CHANGED
@@ -9,6 +9,8 @@ const WPMC_TARGET_MEDIAS = 'media';
9
  const WPMC_SOURCE_CONTENT = 'content';
10
  const WPMC_SOURCE_MEDIAS = 'media';
11
 
 
 
12
  function wpmc_pop_array(items, count) {
13
  var newItems = [];
14
  while ( newItems.length < count && items.length > 0 ) {
@@ -164,11 +166,12 @@ function wpmc_ignore_do(items, totalcount) {
164
  *
165
  */
166
 
167
- var wpmc = wpmc_new_context();
168
-
169
  /**
170
  * Creates a context object that preserves various states and variables for scanning
171
  * @return {object}
 
 
 
172
  */
173
  function wpmc_new_context() {
174
  return {
@@ -181,16 +184,49 @@ function wpmc_new_context() {
181
  isPause: false,
182
  isPendingPause: false,
183
  currentPhase: null,
 
184
 
185
  phases: {
186
  extractReferencesFromPosts: {
187
  init: function () {
188
  this.progress = 0; // Scanned posts count
189
  this.progressPrev = 0;
 
190
  return this;
191
  },
192
  run: function () {
193
- wpmc_extract_references_from(WPMC_SOURCE_CONTENT);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  },
195
  pause: function () {
196
  jQuery('#wpmc_progression').html('<span class="dashicons dashicons-controls-pause"></span> Paused at preparing posts (' +
@@ -207,10 +243,42 @@ function wpmc_new_context() {
207
  init: function () {
208
  this.progress = 0; // Scanned posts count
209
  this.progressPrev = 0;
 
210
  return this;
211
  },
212
  run: function () {
213
- wpmc_extract_references_from(WPMC_SOURCE_MEDIAS);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  },
215
  pause: function () {
216
  jQuery('#wpmc_progression').html('<span class="dashicons dashicons-controls-pause"></span> Paused at preparing medias (' +
@@ -302,7 +370,7 @@ function wpmc_extract_references_from(source) {
302
  if (!wpmc.currentPhase) return; // Aborted
303
  var reply = wpmc_parse_response(response);
304
  if (!reply.success)
305
- return wpmc_handle_error(reply.message);
306
  if (!reply.finished) {
307
  wpmc.currentPhase.progressPrev = wpmc.currentPhase.progress;
308
  wpmc.currentPhase.progress = reply.limit;
@@ -310,7 +378,7 @@ function wpmc_extract_references_from(source) {
310
  else wpmc.currentPhase = wpmc.currentPhase.nextPhase().init();
311
  return wpmc.currentPhase.run();
312
  }).fail(function (e) { // Server Error
313
- wpmc_handle_error(e.statusText, e.status);
314
  });
315
  }, wpmc_cfg.delay
316
  );
@@ -354,7 +422,7 @@ function wpmc_retrieve_targets_for(target, path = null, limit = 0) {
354
  wpmc.currentPhase.method = method;
355
 
356
  if (!reply.success)
357
- return wpmc_handle_error(reply.message);
358
 
359
  // Store results
360
  for (var i = 0, len = reply.results.length; i < len; i++) {
@@ -394,7 +462,7 @@ function wpmc_retrieve_targets_for(target, path = null, limit = 0) {
394
  wpmc.currentPhase.run();
395
 
396
  }).fail(function (e) { // Server Error
397
- wpmc_handle_error(e.statusText, e.status);
398
  });
399
 
400
  }, wpmc_cfg.delay
@@ -449,26 +517,6 @@ function wpmc_parse_response(response) {
449
  return r;
450
  }
451
 
452
- /**
453
- * Pauses the scan and Displays an error dialog
454
- * @param {string} msg=null The error message
455
- * @param {int} status=null The actual status code that the server responded
456
- */
457
- function wpmc_handle_error(msg = null, status = null) {
458
- wpmc.isPendingPause = true;
459
- wpmc_update_to_pause();
460
-
461
- var errDialog = jQuery('#wpmc-error-dialog');
462
- if (!msg) msg = 'An error happened'; // Default error message
463
- if (status) {
464
- console.error('Media Cleaner got an error from server:', status + ' ' + msg);
465
- msg = '<span class="error-status">' + status + '</span> ' + msg;
466
- } else
467
- console.error(msg);
468
- errDialog.find('h3').html(msg);
469
- errDialog.dialog('open');
470
- }
471
-
472
  function wpmc_update_to_error() {
473
  var current = wpmc.total - (wpmc.files.length + wpmc.medias.length);
474
  var totalcount = wpmc.total;
@@ -524,11 +572,11 @@ function wpmc_check_targets() {
524
  }).done(function (response) {
525
  var reply = wpmc_parse_response(response);
526
  if (!reply.success)
527
- return wpmc_handle_error(reply.message);
528
  wpmc.issues += expectedSuccess - reply.results;
529
  wpmc_check_targets();
530
  }).fail(function (e) { // Server Error
531
- wpmc_handle_error(e.statusText, e.status);
532
  });
533
  }, wpmc_cfg.delay
534
  );
@@ -561,12 +609,70 @@ function wpmc_open_dialog(content) {
561
  return dialog;
562
  }
563
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
  /**
565
  *
566
  * INIT
567
  *
568
  */
569
  (function ($) {
 
 
570
 
571
  // Bulk Selection
572
  $('#wpmc-cb-select-all').on('change', function (cb) {
@@ -792,6 +898,27 @@ function wpmc_open_dialog(content) {
792
  wpmc_pause(); // Resume
793
  errDialog.dialog('close');
794
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
  })();
796
 
797
  })(jQuery);
9
  const WPMC_SOURCE_CONTENT = 'content';
10
  const WPMC_SOURCE_MEDIAS = 'media';
11
 
12
+ var wpmc = null; // Global Context
13
+
14
  function wpmc_pop_array(items, count) {
15
  var newItems = [];
16
  while ( newItems.length < count && items.length > 0 ) {
166
  *
167
  */
168
 
 
 
169
  /**
170
  * Creates a context object that preserves various states and variables for scanning
171
  * @return {object}
172
+ *
173
+ * TODO: Make this an actual class
174
+ * TODO: Create 'Phase' class as the base class of each kind of phase
175
  */
176
  function wpmc_new_context() {
177
  return {
184
  isPause: false,
185
  isPendingPause: false,
186
  currentPhase: null,
187
+ errorHandler: new ErrorHandler(),
188
 
189
  phases: {
190
  extractReferencesFromPosts: {
191
  init: function () {
192
  this.progress = 0; // Scanned posts count
193
  this.progressPrev = 0;
194
+ this.progressMax = 0;
195
  return this;
196
  },
197
  run: function () {
198
+ var me = this;
199
+ var src = WPMC_SOURCE_CONTENT;
200
+
201
+ if (!this.progressMax) {
202
+ jQuery.ajax(ajaxurl, {
203
+ type: 'POST',
204
+ dataType: 'text',
205
+ data: {
206
+ action: 'wpmc_get_num_posts',
207
+ source: src
208
+ },
209
+ timeout: wpmc_cfg.timeout + 5000
210
+
211
+ }).done(function (result) {
212
+ result = wpmc_parse_response(result);
213
+ me.progressMax = result.data.num;
214
+ wpmc_extract_references_from(src);
215
+
216
+ }).fail(function (e) {
217
+ wpmc.errorHandler.handle(e.statusText, e.status);
218
+ });
219
+
220
+ return;
221
+ }
222
+
223
+ if (this.progress >= this.progressMax) {
224
+ wpmc.currentPhase = this.nextPhase().init();
225
+ wpmc.currentPhase.run();
226
+ return;
227
+ }
228
+
229
+ wpmc_extract_references_from(src);
230
  },
231
  pause: function () {
232
  jQuery('#wpmc_progression').html('<span class="dashicons dashicons-controls-pause"></span> Paused at preparing posts (' +
243
  init: function () {
244
  this.progress = 0; // Scanned posts count
245
  this.progressPrev = 0;
246
+ this.progressMax = 0;
247
  return this;
248
  },
249
  run: function () {
250
+ var me = this;
251
+ var src = WPMC_SOURCE_MEDIAS;
252
+
253
+ if (!this.progressMax) {
254
+ jQuery.ajax(ajaxurl, {
255
+ type: 'POST',
256
+ dataType: 'text',
257
+ data: {
258
+ action: 'wpmc_get_num_posts',
259
+ source: src
260
+ },
261
+ timeout: wpmc_cfg.timeout + 5000
262
+
263
+ }).done(function (result) {
264
+ result = wpmc_parse_response(result);
265
+ me.progressMax = result.data.num;
266
+ wpmc_extract_references_from(src);
267
+
268
+ }).fail(function (e) {
269
+ wpmc.errorHandler.handle(e.statusText, e.status);
270
+ });
271
+
272
+ return
273
+ }
274
+
275
+ if (this.progress >= this.progressMax) {
276
+ wpmc.currentPhase = this.nextPhase().init();
277
+ wpmc.currentPhase.run();
278
+ return;
279
+ }
280
+
281
+ wpmc_extract_references_from(src);
282
  },
283
  pause: function () {
284
  jQuery('#wpmc_progression').html('<span class="dashicons dashicons-controls-pause"></span> Paused at preparing medias (' +
370
  if (!wpmc.currentPhase) return; // Aborted
371
  var reply = wpmc_parse_response(response);
372
  if (!reply.success)
373
+ return wpmc.errorHandler.handle(reply.message);
374
  if (!reply.finished) {
375
  wpmc.currentPhase.progressPrev = wpmc.currentPhase.progress;
376
  wpmc.currentPhase.progress = reply.limit;
378
  else wpmc.currentPhase = wpmc.currentPhase.nextPhase().init();
379
  return wpmc.currentPhase.run();
380
  }).fail(function (e) { // Server Error
381
+ wpmc.errorHandler.handle(e.statusText, e.status);
382
  });
383
  }, wpmc_cfg.delay
384
  );
422
  wpmc.currentPhase.method = method;
423
 
424
  if (!reply.success)
425
+ return wpmc.errorHandler.handle(reply.message);
426
 
427
  // Store results
428
  for (var i = 0, len = reply.results.length; i < len; i++) {
462
  wpmc.currentPhase.run();
463
 
464
  }).fail(function (e) { // Server Error
465
+ wpmc.errorHandler.handle(e.statusText, e.status);
466
  });
467
 
468
  }, wpmc_cfg.delay
517
  return r;
518
  }
519
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
  function wpmc_update_to_error() {
521
  var current = wpmc.total - (wpmc.files.length + wpmc.medias.length);
522
  var totalcount = wpmc.total;
572
  }).done(function (response) {
573
  var reply = wpmc_parse_response(response);
574
  if (!reply.success)
575
+ return wpmc.errorHandler.handle(reply.message);
576
  wpmc.issues += expectedSuccess - reply.results;
577
  wpmc_check_targets();
578
  }).fail(function (e) { // Server Error
579
+ wpmc.errorHandler.handle(e.statusText, e.status);
580
  });
581
  }, wpmc_cfg.delay
582
  );
609
  return dialog;
610
  }
611
 
612
+ /**
613
+ * @constructor
614
+ */
615
+ function ErrorHandler() {
616
+ this.mode = 'ASK'; // 'RETRY', 'SKIP'
617
+ this.MAX_RETRY = 30; // 30;
618
+ this.RETRY_DELAY = 5; // in sec
619
+ this.nRetried = 0;
620
+ this.nSkipped = 0;
621
+ }
622
+ ErrorHandler.prototype.setMode = function (mode) {
623
+ this.mode = mode;
624
+ return this;
625
+ };
626
+ ErrorHandler.prototype.handle = function (msg = null, status = null) {
627
+ var me = this;
628
+ switch (this.mode) {
629
+ case 'RETRY':
630
+ if (this.nRetried >= this.MAX_RETRY - 1) {
631
+ // Too many retries, switch back to ASK mode
632
+ console.warn('Auto-Retry is turned off due to too many fruitless attempts');
633
+ this.setMode('ASK');
634
+ this.nRetried = 0;
635
+ }
636
+ // Retry after few seconds
637
+ console.log('Retry starts in ' + this.RETRY_DELAY + ' seconds...');
638
+ setTimeout(function () {
639
+ me.nRetried++;
640
+ wpmc.currentPhase.run();
641
+
642
+ }, this.RETRY_DELAY * 1000);
643
+ break;
644
+ case 'SKIP':
645
+ wpmc.currentPhase.skip();
646
+ this.nSkipped++;
647
+ console.warn(this.nSkipped + ' ' + (this.nSkipped > 1 ? 'errors are' : 'error is') + ' ignored automatically');
648
+ wpmc.currentPhase.run();
649
+ break;
650
+ default: // ASK
651
+ wpmc.isPendingPause = true;
652
+ wpmc_update_to_pause();
653
+
654
+ // Show Error Dialog
655
+ var errDialog = jQuery('#wpmc-error-dialog');
656
+ if (!msg) msg = 'An error happened'; // Default error message
657
+ if (status) {
658
+ console.error('Media Cleaner got an error from server:', status + ' ' + msg);
659
+ msg = '<span class="error-status">' + status + '</span> ' + msg;
660
+ } else
661
+ console.error(msg);
662
+
663
+ errDialog.find('h3').html(msg);
664
+ errDialog.dialog('open');
665
+ }
666
+ };
667
+
668
  /**
669
  *
670
  * INIT
671
  *
672
  */
673
  (function ($) {
674
+ // Initialize Global Context
675
+ wpmc = wpmc_new_context();
676
 
677
  // Bulk Selection
678
  $('#wpmc-cb-select-all').on('change', function (cb) {
898
  wpmc_pause(); // Resume
899
  errDialog.dialog('close');
900
  });
901
+
902
+ // Always Retry
903
+ errDialog.find('a.always-retry').on('click', function (ev) {
904
+ ev.preventDefault();
905
+
906
+ wpmc.errorHandler.setMode('RETRY');
907
+
908
+ wpmc_pause(); // Resume
909
+ errDialog.dialog('close');
910
+ });
911
+
912
+ // Skip All
913
+ errDialog.find('a.skip-all').on('click', function (ev) {
914
+ ev.preventDefault();
915
+
916
+ wpmc.errorHandler.setMode('SKIP');
917
+
918
+ wpmc_pause(); // Resume
919
+ errDialog.dialog('close');
920
+ });
921
+
922
  })();
923
 
924
  })(jQuery);
scripts/style.css CHANGED
@@ -55,6 +55,15 @@
55
  margin-left: 5px;
56
  }
57
 
 
 
 
 
 
 
 
 
 
58
  /* Settings */
59
  .wrap-media-cleaner .meow-box form input:invalid {
60
  color: red;
55
  margin-left: 5px;
56
  }
57
 
58
+ /* Error Dialog */
59
+ #wpmc-error-dialog .options {
60
+ margin-top: 20px;
61
+ text-align: right;
62
+ }
63
+ #wpmc-error-dialog .options a + a {
64
+ margin-left: 16px;
65
+ }
66
+
67
  /* Settings */
68
  .wrap-media-cleaner .meow-box form input:invalid {
69
  color: red;
views/menu-screen.php CHANGED
@@ -249,6 +249,10 @@
249
  if ( class_exists( 'ImageMapPro' ) )
250
  array_push( $unsupported, 'Image Map Pro' );
251
 
 
 
 
 
252
  if ( !empty( $unsupported ) ) {
253
  echo "<div class='notice notice-error'><p>";
254
  _e( "<b>Important note about the following plugin(s): </b>", 'media-cleaner' );
@@ -470,4 +474,8 @@
470
  <div id="wpmc-error-dialog" class="hidden" style="max-width:800px">
471
  <h3><!-- The content will be inserted by JS --></h3>
472
  <p>Please check your logs.<br>Do you want to <a href="#" class="retry">try again</a>, or <a href="#" class="skip">skip this entry</a>?</p>
 
 
 
 
473
  </div>
249
  if ( class_exists( 'ImageMapPro' ) )
250
  array_push( $unsupported, 'Image Map Pro' );
251
 
252
+ if ( class_exists( 'YOOtheme\Builder\Wordpress\BuilderListener' ) ) {
253
+ array_push( $unsupported, 'YooTheme Builder' );
254
+ }
255
+
256
  if ( !empty( $unsupported ) ) {
257
  echo "<div class='notice notice-error'><p>";
258
  _e( "<b>Important note about the following plugin(s): </b>", 'media-cleaner' );
474
  <div id="wpmc-error-dialog" class="hidden" style="max-width:800px">
475
  <h3><!-- The content will be inserted by JS --></h3>
476
  <p>Please check your logs.<br>Do you want to <a href="#" class="retry">try again</a>, or <a href="#" class="skip">skip this entry</a>?</p>
477
+ <div class="options">
478
+ <a href="#" class="always-retry"><?php _e( 'Always Retry', 'media-cleaner' ) ?></a>
479
+ <a href="#" class="skip-all"><?php _e( 'Skip All', 'media-cleaner' ) ?></a>
480
+ </div>
481
  </div>