Media Cleaner - Version 4.0.2

Version Description

  • Add: Information about how a certain media is used (Edit Media screen).
  • Fix: Check / Create DB process.
  • Fix: Plugin was not working well with themes using Background/Header.
  • Update: A bit of cleaning.
Download this release

Release Info

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

Code changes from version 4.0.0 to 4.0.2

core.php CHANGED
@@ -6,6 +6,9 @@ class Meow_WPMC_Core {
6
  public $admin = null;
7
  public $debug = false;
8
 
 
 
 
9
  public function __construct( $admin ) {
10
  $this->admin = $admin;
11
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
@@ -20,11 +23,49 @@ class Meow_WPMC_Core {
20
  add_action( 'wp_ajax_wpmc_recover_do', array( $this, 'wp_ajax_wpmc_recover_do' ) );
21
  add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 10, 2 );
22
 
 
 
 
 
23
  // Checkers
24
  require( 'wpmc_checkers.php' );
25
  $this->checkers = new Meow_WPMC_Checkers( $this );
26
  }
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  /*******************************************************************************
29
  * ASYNCHRONOUS AJAX FUNCTIONS
30
  ******************************************************************************/
@@ -254,6 +295,14 @@ class Meow_WPMC_Core {
254
  return trailingslashit( $upload_folder['basedir'] ) . 'wpmc-trash';
255
  }
256
 
 
 
 
 
 
 
 
 
257
  /**
258
  *
259
  * DELETE / SCANNING / RESET
@@ -476,7 +525,7 @@ class Meow_WPMC_Core {
476
  $table_name = $wpdb->prefix . "wpmcleaner";
477
  $count = $wpdb->get_var( "SELECT COUNT(*)
478
  FROM $table_name
479
- WHERE deleted = 0
480
  AND path LIKE '%". esc_sql( $wpdb->esc_like( $file ) ) . "%'" );
481
  if ( $this->debug && $count > 0 )
482
  error_log("{$file} found in IGNORE");
@@ -532,6 +581,8 @@ class Meow_WPMC_Core {
532
 
533
  function wpmc_check_media( $attachmentId ) {
534
 
 
 
535
  // Is it an image?
536
  $meta = wp_get_attachment_metadata( $attachmentId );
537
  $isImage = isset( $meta, $meta['width'], $meta['height'] );
@@ -548,12 +599,28 @@ class Meow_WPMC_Core {
548
  if ( file_exists( $fullpath ) ) {
549
  $size = filesize( $fullpath );
550
 
551
- if ( $this->wpmc_check_is_ignore( $mainfile )
552
- || $this->checkers->wpmc_check_db_has_background_or_header( $mainfile )
553
- || $this->checkers->wpmc_check_db_has_content( $mainfile, $attachmentId )
554
- || $this->checkers->wpmc_check_in_gallery( $mainfile )
555
- || $this->checkers->wpmc_check_db_has_meta( $mainfile, $attachmentId ) )
 
 
 
556
  return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
  // If images, check the other files as well
559
  $countfiles = 0;
@@ -569,11 +636,23 @@ class Meow_WPMC_Core {
569
  }
570
  $file = $this->wpmc_clean_uploaded_filename( $attr['file'] );
571
  $countfiles++;
572
- if ($this->debug) error_log("checking MEDIA-IMAGE {$filepath}");
573
- if ( $this->checkers->wpmc_check_in_gallery( $filepath )
574
- || $this->checkers->wpmc_check_db_has_background_or_header( $filepath )
575
- || $this->checkers->wpmc_check_db_has_meta( $filepath, $attachmentId ) )
 
 
576
  return true;
 
 
 
 
 
 
 
 
 
 
577
  }
578
  }
579
  }
@@ -656,6 +735,7 @@ class Meow_WPMC_Core {
656
 
657
  function wpmc_screen() {
658
  global $wplr;
 
659
  ?>
660
  <div class='wrap'>
661
 
@@ -939,14 +1019,6 @@ register_activation_hook( __FILE__, 'wpmc_activate' );
939
  register_deactivation_hook( __FILE__, 'wpmc_uninstall' );
940
  register_uninstall_hook( __FILE__, 'wpmc_uninstall' );
941
 
942
- function wpmc_check_db() {
943
- global $wpdb;
944
- $tbl_m = $wpdb->prefix . 'wpmcleaner';
945
- if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = '%s' AND table_name = '%s';", $wpdb->dbname, $tbl_m ) ) ) {
946
- wpmc_activate();
947
- }
948
- }
949
-
950
  function wpmc_reset () {
951
  wpmc_uninstall();
952
  wpmc_activate();
6
  public $admin = null;
7
  public $debug = false;
8
 
9
+ public $last_analysis = null;
10
+ public $last_analysis_ids = null;
11
+
12
  public function __construct( $admin ) {
13
  $this->admin = $admin;
14
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
23
  add_action( 'wp_ajax_wpmc_recover_do', array( $this, 'wp_ajax_wpmc_recover_do' ) );
24
  add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 10, 2 );
25
 
26
+ if ( is_admin() ) {
27
+ add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
28
+ }
29
+
30
  // Checkers
31
  require( 'wpmc_checkers.php' );
32
  $this->checkers = new Meow_WPMC_Checkers( $this );
33
  }
34
 
35
+ /*******************************************************************************
36
+ * METABOX FOR USAGE
37
+ ******************************************************************************/
38
+
39
+ function add_metabox() {
40
+ add_meta_box( 'mfrh_media_usage_box', 'Media Cleaner', array( $this, 'display_metabox' ), 'attachment', 'side', 'default' );
41
+ }
42
+
43
+ function display_metabox( $post ) {
44
+ if ( $this->wpmc_check_media( $post->ID ) ) {
45
+ if ( $this->last_analysis == "CONTENT" ) {
46
+ $result = array();
47
+ foreach ( $this->last_analysis_ids as $id )
48
+ array_push( $result, "<a href='post.php?post=$id&action=edit'>$id</a>" );
49
+ echo "Seems to be used in your content (" . implode( ', ', $result ) . ").";
50
+ }
51
+ else if ( $this->last_analysis == "BACKGROUND_OR_HEADER" ) {
52
+ echo "Seems to be used as a background or header.";
53
+ }
54
+ else if ( $this->last_analysis == "GALLERY" ) {
55
+ echo "Seems to be used in a gallery.";
56
+ }
57
+ else if ( $this->last_analysis == "META" ) {
58
+ echo "Seems to be used as a meta.";
59
+ }
60
+ else {
61
+ echo "Used: " . $this->last_analysis;
62
+ }
63
+ }
64
+ else {
65
+ echo "Doesn't seem to be used.";
66
+ }
67
+ }
68
+
69
  /*******************************************************************************
70
  * ASYNCHRONOUS AJAX FUNCTIONS
71
  ******************************************************************************/
295
  return trailingslashit( $upload_folder['basedir'] ) . 'wpmc-trash';
296
  }
297
 
298
+ function wpmc_check_db() {
299
+ global $wpdb;
300
+ $tbl_m = $wpdb->prefix . 'wpmcleaner';
301
+ if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = '%s' AND table_name = '%s';", $wpdb->dbname, $tbl_m ) ) ) {
302
+ wpmc_activate();
303
+ }
304
+ }
305
+
306
  /**
307
  *
308
  * DELETE / SCANNING / RESET
525
  $table_name = $wpdb->prefix . "wpmcleaner";
526
  $count = $wpdb->get_var( "SELECT COUNT(*)
527
  FROM $table_name
528
+ WHERE ignored = 1
529
  AND path LIKE '%". esc_sql( $wpdb->esc_like( $file ) ) . "%'" );
530
  if ( $this->debug && $count > 0 )
531
  error_log("{$file} found in IGNORE");
581
 
582
  function wpmc_check_media( $attachmentId ) {
583
 
584
+ $this->last_analysis = "N/A";
585
+
586
  // Is it an image?
587
  $meta = wp_get_attachment_metadata( $attachmentId );
588
  $isImage = isset( $meta, $meta['width'], $meta['height'] );
599
  if ( file_exists( $fullpath ) ) {
600
  $size = filesize( $fullpath );
601
 
602
+ // ANALYSIS
603
+ $this->last_analysis = "NONE";
604
+ if ( $this->wpmc_check_is_ignore( $mainfile ) ) {
605
+ $this->last_analysis = "IGNORED";
606
+ return true;
607
+ }
608
+ if ( $this->checkers->has_background_or_header( $mainfile ) ) {
609
+ $this->last_analysis = "BACKGROUND_OR_HEADER";
610
  return true;
611
+ }
612
+ if ( $this->checkers->has_content( $mainfile ) ) {
613
+ $this->last_analysis = "CONTENT";
614
+ return true;
615
+ }
616
+ if ( $this->checkers->wpmc_check_in_gallery( $mainfile ) ) {
617
+ $this->last_analysis = "GALLERY";
618
+ return true;
619
+ }
620
+ if ( $this->checkers->has_meta( $mainfile, $attachmentId ) ) {
621
+ $this->last_analysis = "META";
622
+ return true;
623
+ }
624
 
625
  // If images, check the other files as well
626
  $countfiles = 0;
636
  }
637
  $file = $this->wpmc_clean_uploaded_filename( $attr['file'] );
638
  $countfiles++;
639
+ if ( $this->debug )
640
+ error_log("checking MEDIA-IMAGE {$filepath}");
641
+
642
+ // ANALYSIS
643
+ if ( $this->checkers->wpmc_check_in_gallery( $filepath ) ) {
644
+ $this->last_analysis = "GALLERY";
645
  return true;
646
+ }
647
+ if ( $this->checkers->has_background_or_header( $filepath ) ) {
648
+ $this->last_analysis = "BACKGROUND_OR_HEADER";
649
+ return true;
650
+ }
651
+ if ( $this->checkers->has_meta( $filepath, $attachmentId ) ) {
652
+ $this->last_analysis = "META";
653
+ return true;
654
+ }
655
+
656
  }
657
  }
658
  }
735
 
736
  function wpmc_screen() {
737
  global $wplr;
738
+ $this->wpmc_check_db();
739
  ?>
740
  <div class='wrap'>
741
 
1019
  register_deactivation_hook( __FILE__, 'wpmc_uninstall' );
1020
  register_uninstall_hook( __FILE__, 'wpmc_uninstall' );
1021
 
 
 
 
 
 
 
 
 
1022
  function wpmc_reset () {
1023
  wpmc_uninstall();
1024
  wpmc_activate();
media-cleaner.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Media Cleaner
4
  Plugin URI: http://meowapps.com
5
  Description: Clean your Media Library, many options, trash system.
6
- Version: 4.0.0
7
  Author: Jordy Meow
8
  Author URI: http://meowapps.com
9
  Text Domain: media-cleaner
@@ -19,7 +19,7 @@ Originally developed for two of my websites:
19
  if ( is_admin() ) {
20
 
21
  global $wpmc_version;
22
- $wpmc_version = '4.0.0';
23
 
24
  // Admin
25
  require( 'wpmc_admin.php' );
3
  Plugin Name: Media Cleaner
4
  Plugin URI: http://meowapps.com
5
  Description: Clean your Media Library, many options, trash system.
6
+ Version: 4.0.2
7
  Author: Jordy Meow
8
  Author URI: http://meowapps.com
9
  Text Domain: media-cleaner
19
  if ( is_admin() ) {
20
 
21
  global $wpmc_version;
22
+ $wpmc_version = '4.0.2';
23
 
24
  // Admin
25
  require( 'wpmc_admin.php' );
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
4
  Tags: management, admin, file, files, images, image, media, library, upload, clean, cleaning
5
  Requires at least: 4.2
6
  Tested up to: 4.7
7
- Stable tag: 4.0.0
8
 
9
  Clean your Media Library and Uploads directory. It has an internal trash and recovery features.
10
 
@@ -51,6 +51,12 @@ It re-creates the Media Cleaner table in the database. You will need to re-run t
51
 
52
  == Changelog ==
53
 
 
 
 
 
 
 
54
  = 4.0.0 =
55
  * Update: Core was re-organized and cleaned. Ready for nice updates.
56
 
4
  Tags: management, admin, file, files, images, image, media, library, upload, clean, cleaning
5
  Requires at least: 4.2
6
  Tested up to: 4.7
7
+ Stable tag: 4.0.2
8
 
9
  Clean your Media Library and Uploads directory. It has an internal trash and recovery features.
10
 
51
 
52
  == Changelog ==
53
 
54
+ = 4.0.2 =
55
+ * Add: Information about how a certain media is used (Edit Media screen).
56
+ * Fix: Check / Create DB process.
57
+ * Fix: Plugin was not working well with themes using Background/Header.
58
+ * Update: A bit of cleaning.
59
+
60
  = 4.0.0 =
61
  * Update: Core was re-organized and cleaned. Ready for nice updates.
62
 
trunk/common/admin.css ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* MEOW BOX (SETTINGS BOX) */
2
+
3
+ .meow-box {
4
+ box-sizing: border-box;
5
+ border: 1px solid #e5e5e5;
6
+ box-shadow: 2px 2px 1px rgba(0,0,0,.02);
7
+ background: #fff;
8
+ color: #444;
9
+ margin-bottom: 15px;
10
+ font-size: 13px !important;
11
+ border-top-right-radius: 8px;
12
+ }
13
+
14
+ .meow-box input, .meow-box th, .meow-box label, .meow-box select {
15
+ font-size: 13px !important;
16
+ }
17
+
18
+ .meow-box small {
19
+ font-size: 11px !important;
20
+ }
21
+
22
+ .meow-box h3 {
23
+ font-size: 13px;
24
+ padding: 4px 12px;
25
+ margin: 0;
26
+ background: #3c3c3c;
27
+ color: #ffffff;
28
+ text-transform: uppercase;
29
+ border-top-right-radius: 8px;
30
+ /*border-bottom: 1px solid #eee;*/
31
+ }
32
+
33
+ .meow-box h3 .dashicons {
34
+ position: relative;
35
+ top: 0px;
36
+ margin-right: 5px;
37
+ }
38
+
39
+ .meow-box .pro_info {
40
+ padding: 5px;
41
+ margin: 10px -10px 5px -10px;
42
+ font-size: 11px;
43
+ line-height: 13px;
44
+ }
45
+
46
+ .meow-box .pro_info.enabled {
47
+ background: #4482d2;
48
+ border-left: 5px solid #4482d2;
49
+ color: white;
50
+ }
51
+
52
+ .meow-box .pro_info.disabled {
53
+ background: #96555b;
54
+ border-left: 5px solid #632329;
55
+ color: white;
56
+ }
57
+
58
+ .meow-box .pro_info.disabled a {
59
+ background: #96555b;
60
+ color: red;
61
+ text-decoration: none;
62
+ }
63
+
64
+ .meow-box .inside {
65
+ margin: 10px;
66
+ }
67
+
68
+ .meow-box th {
69
+ padding: 10px 10px 10px 0px;
70
+ width: 22%;
71
+ }
72
+
73
+ .meow-box td {
74
+ padding: 10px 10px;
75
+ }
76
+
77
+ .meow-box p.submit {
78
+ text-align: right;
79
+ margin: 10px -10px -10px -10px;
80
+ padding: 7px 10px 10px 0px !important;
81
+ border-top: 1px solid #eee !important;
82
+ max-width: inherit;
83
+ background: rgba(125, 125, 125, 0.04);
84
+ border-radius: 0px;
85
+ }
86
+
87
+ .meow-box [type="text"], .meow-box select {
88
+ width: 100%;
89
+ }
90
+
91
+ /* CONTROLS INSIDE A COLUMN (LABEL ON THE LEFT, VALUE ON THE RIGHT) */
92
+ .meow-box td [type="text"], .meow-box td [type="checkbox"], .meow-box td select {
93
+ margin-top: -3px;
94
+ }
95
+
96
+ .meow-header-ad {
97
+ float: right;
98
+ }
99
+
100
+ /* BUTTONS */
101
+
102
+ .meow-button-xs {
103
+ font-size: 10px !important;
104
+ height: 20px !important;
105
+ line-height: 18px !important;
106
+ position: relative !important;
107
+ top: 1px !important;
108
+ text-align: center !important
109
+ }
110
+
111
+ /* MODAL */
112
+
113
+ #meow-modal-info-backdrop {
114
+ background: rgba(0, 0, 0, 0.75);
115
+ position: fixed;
116
+ top: 0px;
117
+ bottom: 0px;
118
+ right: 0px;
119
+ left: 0px;
120
+ z-index: 10000;
121
+ }
122
+
123
+ #meow-modal-info {
124
+ background: white;
125
+ box-shadow: 0px 0px 5px black;
126
+ padding: 15px;
127
+ overflow-y: scroll;
128
+ position: fixed;
129
+ z-index: 10000;
130
+ left: 100px;
131
+ right: 100px;
132
+ top: 50px;
133
+ bottom: 50px;
134
+ }
135
+
136
+ #meow-modal-info h3 {
137
+ height: 25px;
138
+ border-bottom: 2px solid #808080;
139
+ }
140
+
141
+ #meow-modal-info td {
142
+ text-align: center;
143
+ font-size: 11px;
144
+ border: 1px solid #D3DCFF;
145
+ padding: 10px 15px;
146
+ background: #EFF8FF;
147
+ }
148
+
149
+ #meow-modal-info .close {
150
+ float: right;
151
+ font-size: 18px;
152
+ font-weight: bold;
153
+ font-family: Verdana;
154
+ cursor: pointer;
155
+ }
156
+
157
+ #meow-modal-info .loading {
158
+ background-color: #F2F2F2;
159
+ text-align: center;
160
+ padding-top: 10px;
161
+ background-size: 32px 32px;
162
+ }
163
+
164
+ #meow-modal-info .meow-sized-image {
165
+ width: 19px;
166
+ height: 19px;
167
+ margin-right: 10px;
168
+ }
169
+
170
+ /* IMAGE SIZES RELATED INFO */
171
+
172
+ .meow-sized-images {
173
+ margin-top: 0px;
174
+ cursor: pointer;
175
+ }
176
+
177
+ .meow-sized-images li, #meow-modal-info .meow-sized-image {
178
+ display: block;
179
+ color: white;
180
+ padding: 5px 0px;
181
+ font-size: 10px;
182
+ text-align: center;
183
+ width: 18px;
184
+ height: 18px;
185
+ line-height: 8px;
186
+ background: black;
187
+ float: left;
188
+ margin-right: 1px;
189
+ box-sizing: border-box;
190
+ margin-bottom: 2px;
191
+ }
192
+
193
+ .meow-sized-images:after {
194
+ clear: both;
195
+ content:""; display:table;
196
+ }
197
+
198
+ .meow-sized-images + span {
199
+ position: relative;
200
+ top: -12px;
201
+ }
202
+
203
+ /* MEOW COLORS */
204
+
205
+ .meow-bk-blue {
206
+ background: #3C82C7 !important;
207
+ color: white;
208
+ }
209
+
210
+ .meow-bk-orange {
211
+ background: #f1900e !important;
212
+ color: white;
213
+ }
214
+
215
+ .meow-bk-red {
216
+ background: #c53a47 !important;
217
+ color: white;
218
+ }
219
+
220
+ .meow-bk-gray {
221
+ background: gray !important;
222
+ color: white;
223
+ }
224
+
225
+ .meow-bk-green {
226
+ background: #2b9463 !important;
227
+ color: white;
228
+ }
229
+
230
+ .meow-bk-black {
231
+ background: #3c3c3c !important;
232
+ color: white;
233
+ }
234
+
235
+ .meow-bk-purple {
236
+ background: #984c96 !important;
237
+ color: white;
238
+ }
239
+
240
+ /* DASHBOARD */
241
+
242
+ .meow-dashboard .meow-box li {
243
+ border-bottom: 1px solid #eee;
244
+ padding: 0px 10px 10px 10px;
245
+ }
246
+
247
+ .meow-dashboard .meow-box li:last-child {
248
+ border: none;
249
+ padding-bottom: 0px;
250
+ }
251
+
252
+ .meow-button-xs .dashicons {
253
+ font-size: 18px !important;
254
+ }
255
+
256
+ .meow-button-xs.updating-message:before {
257
+ margin-top: 0px !important;
258
+ }
259
+
260
+ .meow-button-xs.updating-message * {
261
+ display: none;
262
+ }
263
+
264
+ #phpinfo {
265
+ font-size: 11px;
266
+ }
267
+
268
+ #phpinfo .e {
269
+ padding: 5px;
270
+ background: lightgray;
271
+ }
272
+
273
+ #phpinfo .h th {
274
+ padding: 5px;
275
+ color: white;
276
+ background: gray;
277
+ text-align: left;
278
+ font-size: 14px !important;
279
+ }
280
+
281
+ #phpinfo h1 {
282
+ padding: 10px 0px;
283
+ font-weight: bold;
284
+ }
285
+
286
+ #phpinfo h2 {
287
+ padding: 10px 0px;
288
+ font-weight: bold;
289
+ font-size: 20px;
290
+ }
291
+
292
+ #error_log {
293
+ font-size: 12px;
294
+ }
295
+
296
+ /* ROWS AND COLUMNS */
297
+ .meow-col { display: block; float:left; margin: 1% 0 1% 1.6%; }
298
+ .meow-col:first-child { margin-left: 0; }
299
+
300
+ /* GROUPING */
301
+ .meow-row { clear: both; padding: 0px; margin: 0px; }
302
+ .meow-row:before,
303
+ .meow-row:after { content:""; display:table; }
304
+ .meow-row:after { clear:both; }
305
+ .meow-row { zoom:1; /* For IE 6/7 */ }
306
+
307
+ /* GRID OF THREE */
308
+ .meow-span_3_of_3 { width: 100%; }
309
+ .meow-span_2_of_3 { width: 66.1%; }
310
+ .meow-span_1_of_3 { width: 32.2%; }
311
+
312
+ /* GRID OF TWO */
313
+ .meow-span_2_of_2 { width: 100%; }
314
+ .meow-span_1_of_2 { width: 49.2%; }
315
+
316
+ @media only screen and (max-width: 480px) {
317
+ .meow-col { margin: 0 0 0px 0;}
318
+ .meow-span_3_of_3, .meow-span_2_of_3, .meow-span_1_of_3 { width: 100%; }
319
+ .meow-span_2_of_2, .meow-span_1_of_2 { width: 100%; }
320
+ .meow-header-ad { display: none !important; }
321
+ .meow-box [type="text"] { width: 100% !important; }
322
+ }
trunk/common/admin.php ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( !class_exists( 'MeowApps_Admin' ) ) {
4
+
5
+ class MeowApps_Admin {
6
+
7
+ public static $loaded = false;
8
+ public static $admin_version = "1.2";
9
+
10
+ public $prefix; // prefix used for actions, filters (mfrh)
11
+ public $mainfile; // plugin main file (media-file-renamer.php)
12
+ public $domain; // domain used for translation (media-file-renamer)
13
+
14
+ public function __construct( $prefix, $mainfile, $domain ) {
15
+
16
+ // Core Admin (used by all Meow Apps plugins)
17
+ if ( !MeowApps_Admin::$loaded ) {
18
+ if ( is_admin() ) {
19
+ add_action( 'admin_menu', array( $this, 'admin_menu_start' ) );
20
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
21
+ add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) );
22
+ }
23
+ MeowApps_Admin::$loaded = true;
24
+ }
25
+
26
+ // Variables for this plugin
27
+ $this->prefix = $prefix;
28
+ $this->mainfile = $mainfile;
29
+ $this->domain = $domain;
30
+
31
+ // Check if the free version is installed but there is license
32
+ // TODO: In the future, this should be removed ideally
33
+ if ( is_admin() ) {
34
+ $license = get_option( $this->prefix . '_license', "" );
35
+ if ( ( !empty( $license ) ) && !file_exists( plugin_dir_path( $this->mainfile ) . 'common/meowapps/admin.php' ) ) {
36
+ add_action( 'admin_notices', array( $this, 'admin_notices_licensed_free' ) );
37
+ }
38
+ }
39
+ }
40
+
41
+ function admin_notices_licensed_free() {
42
+ if ( isset( $_POST[$this->prefix . '_reset_sub'] ) ) {
43
+ delete_option( $this->prefix . '_pro_serial' );
44
+ delete_option( $this->prefix . '_license' );
45
+ return;
46
+ }
47
+ echo '<div class="error">';
48
+ echo '<p>It looks like you are using the free version of the plugin (<b>' . $this->mainfile . '</b>) but a license for the Pro version was also found. The Pro version might have been replaced by the Free version during an update (might be caused by a temporarily issue). If it is the case, <b>please download it again</b> from the <a target="_blank" href="https://store.meowapps.com">Meow Store</a>. If you wish to continue using the free version and clear this message, click on this button.';
49
+ echo '<p>
50
+ <form method="post" action="">
51
+ <input type="hidden" name="' . $this->prefix . '_reset_sub" value="true">
52
+ <input type="submit" name="submit" id="submit" class="button" value="Remove the license">
53
+ </form>
54
+ </p>
55
+ ';
56
+ echo '</div>';
57
+ }
58
+
59
+ function display_ads() {
60
+ return !get_option( 'meowapps_hide_ads', false );
61
+ }
62
+
63
+ function display_title( $title = "Meow Apps",
64
+ $author = "By <a style='text-decoration: none;' href='http://meowapps.com' target='_blank'>Jordy Meow</a>" ) {
65
+ if ( !empty( $this->prefix ) )
66
+ $title = apply_filters( $this->prefix . '_plugin_title', $title );
67
+ if ( $this->display_ads() ) {
68
+ echo '<a class="meow-header-ad" target="_blank" href="http://www.shareasale.com/r.cfm?b=906810&u=767054&m=41388&urllink=&afftrack="">
69
+ <img src="' . $this->common_url( 'img/wpengine.png' ) . '" height="60" border="0" /></a>';
70
+ }
71
+ ?>
72
+ <h1 style="line-height: 16px;">
73
+ <img width="36" style="margin-right: 10px; float: left; position: relative; top: -5px;"
74
+ src="<?php echo $this->meowapps_logo_url(); ?>"><?php echo $title; ?><br />
75
+ <span style="font-size: 12px"><?php echo $author; ?></span>
76
+ </h1>
77
+ <div style="clear: both;"></div>
78
+ <?php
79
+ }
80
+
81
+ function admin_enqueue_scripts() {
82
+ wp_register_style( 'meowapps-core-css', $this->common_url( 'admin.css' ) );
83
+ wp_enqueue_style( 'meowapps-core-css' );
84
+ }
85
+
86
+ function admin_menu_start() {
87
+ if ( get_option( 'meowapps_hide_meowapps', false ) ) {
88
+ register_setting( 'general', 'meowapps_hide_meowapps' );
89
+ add_settings_field( 'meowapps_hide_ads', 'Meow Apps Menu', array( $this, 'meowapps_hide_dashboard_callback' ), 'general' );
90
+ return;
91
+ }
92
+
93
+ // Creates standard menu if it does NOT exist
94
+ global $submenu;
95
+ if ( !isset( $submenu[ 'meowapps-main-menu' ] ) ) {
96
+ add_menu_page( 'Meow Apps', 'Meow Apps', 'manage_options', 'meowapps-main-menu',
97
+ array( $this, 'admin_meow_apps' ), 'dashicons-camera', 82 );
98
+ add_submenu_page( 'meowapps-main-menu', __( 'Dashboard', 'meowapps' ),
99
+ __( 'Dashboard', 'meowapps' ), 'manage_options',
100
+ 'meowapps-main-menu', array( $this, 'admin_meow_apps' ) );
101
+ }
102
+
103
+ add_settings_section( 'meowapps_common_settings', null, null, 'meowapps_common_settings-menu' );
104
+ add_settings_field( 'meowapps_hide_meowapps', "Main Menu",
105
+ array( $this, 'meowapps_hide_dashboard_callback' ),
106
+ 'meowapps_common_settings-menu', 'meowapps_common_settings' );
107
+ add_settings_field( 'meowapps_hide_ads', "Ads",
108
+ array( $this, 'meowapps_hide_ads_callback' ),
109
+ 'meowapps_common_settings-menu', 'meowapps_common_settings' );
110
+ register_setting( 'meowapps_common_settings', 'meowapps_hide_meowapps' );
111
+ register_setting( 'meowapps_common_settings', 'meowapps_hide_ads' );
112
+ }
113
+
114
+ function meowapps_hide_ads_callback() {
115
+ $value = get_option( 'meowapps_hide_ads', null );
116
+ $html = '<input type="checkbox" id="meowapps_hide_ads" name="meowapps_hide_ads" value="1" ' .
117
+ checked( 1, get_option( 'meowapps_hide_ads' ), false ) . '/>';
118
+ $html .= __( '<label>Hide</label><br /><small>Doesn\'t display the ads.</small>', 'wp-retina-2x' );
119
+ echo $html;
120
+ }
121
+
122
+ function meowapps_hide_dashboard_callback() {
123
+ $value = get_option( 'meowapps_hide_meowapps', null );
124
+ $html = '<input type="checkbox" id="meowapps_hide_meowapps" name="meowapps_hide_meowapps" value="1" ' .
125
+ checked( 1, get_option( 'meowapps_hide_meowapps' ), false ) . '/>';
126
+ $html .= __( '<label>Hide <b>Meow Apps</b> Menu</label><br /><small>Hide Meow Apps menu and all its components, for a nicer an faster WordPress admin UI. An option will be added in Settings > General to display it again.</small>', 'wp-retina-2x' );
127
+ echo $html;
128
+ }
129
+
130
+ function display_serialkey_box( $url = "https://meowapps.com/" ) {
131
+ $html = '<div class="meow-box">';
132
+ $html .= '<h3 class="' . ( $this->is_registered( $this->prefix ) ? 'meow-bk-blue' : 'meow-bk-red' ) . '">Pro Version ' .
133
+ ( $this->is_registered( $this->prefix ) ? '(enabled)' : '(disabled)' ) . '</h3>';
134
+ $html .= '<div class="inside">';
135
+ echo $html;
136
+ $html = apply_filters( $this->prefix . '_meowapps_license_input', ( 'More information about the Pro version here:
137
+ <a target="_blank" href="' . $url . '">' . $url . '</a>.' ), $url );
138
+ $html .= '</div>';
139
+ $html .= '</div>';
140
+ echo $html;
141
+ }
142
+
143
+ function is_registered() {
144
+ return apply_filters( $this->prefix . '_meowapps_is_registered', false, $this->prefix );
145
+ }
146
+
147
+ function check_install( $plugin ) {
148
+ $pro = false;
149
+ $pluginpath = get_home_path() . 'wp-content/plugins/' . $plugin . '-pro';
150
+ if ( !file_exists( $pluginpath ) ) {
151
+ $pluginpath = get_home_path() . 'wp-content/plugins/' . $plugin;
152
+ if ( !file_exists( $pluginpath ) ) {
153
+ $url = wp_nonce_url( "update.php?action=install-plugin&plugin=$plugin", "install-plugin_$plugin" );
154
+ return "<a href='$url'><small><span class='' style='float: right;'>install</span></small></a>";
155
+ }
156
+ }
157
+ else {
158
+ $pro = true;
159
+ $plugin = $plugin . "-pro";
160
+ }
161
+
162
+ $plugin_file = $plugin . '/' . $plugin . '.php';
163
+ if ( is_plugin_active( $plugin_file ) ) {
164
+ if ( $pro )
165
+ return "<small><span style='float: right;'><span class='dashicons dashicons-heart' style='color: rgba(255, 63, 0, 1); font-size: 30px !important; margin-right: 10px;'></span></span></small>";
166
+ else
167
+ return "<small><span style='float: right;'><span class='dashicons dashicons-yes' style='color: #00b4ff; font-size: 30px !important; margin-right: 10px;'></span></span></small>";
168
+ }
169
+ else {
170
+ $url = wp_nonce_url( self_admin_url( 'plugins.php?action=activate&plugin=' . $plugin_file ),
171
+ 'activate-plugin_' . $plugin_file );
172
+ return '<small><span style="color: black; float: right;">off
173
+ (<a style="color: rgba(30,140,190,1); text-decoration: none;" href="' .
174
+ $url . '">enable</a>)</span></small>';
175
+ }
176
+ }
177
+
178
+ function common_url( $file ) {
179
+ die( "Meow Apps: The function common_url( \$file ) needs to be overriden." );
180
+ // Normally, this should be used:
181
+ // return plugin_dir_url( __FILE__ ) . ( '\/common\/' . $file );
182
+ }
183
+
184
+ function meowapps_logo_url() {
185
+ return $this->common_url( 'img/meowapps.png' );
186
+ }
187
+
188
+ function plugins_loaded() {
189
+ if ( isset( $_GET[ 'tool' ] ) && $_GET[ 'tool' ] == 'error_log' ) {
190
+ $sec = "5";
191
+ header("Refresh: $sec;");
192
+ }
193
+ }
194
+
195
+ function admin_meow_apps() {
196
+
197
+ echo '<div class="wrap meow-dashboard">';
198
+ if ( isset( $_GET['tool'] ) && $_GET['tool'] == 'phpinfo' ) {
199
+ echo "<a href=\"javascript:history.go(-1)\">< Go back</a><br /><br />";
200
+ echo '<div id="phpinfo">';
201
+ ob_start();
202
+ phpinfo();
203
+ $pinfo = ob_get_contents();
204
+ ob_end_clean();
205
+ $pinfo = preg_replace( '%^.*<body>(.*)</body>.*$%ms','$1', $pinfo );
206
+ echo $pinfo;
207
+ echo "</div>";
208
+ }
209
+ else if ( isset( $_GET['tool'] ) && $_GET['tool'] == 'error_log' ) {
210
+ $errorpath = ini_get( 'error_log' );
211
+ echo "<a href=\"javascript:history.go(-1)\">< Go back</a><br /><br />";
212
+ echo '<div id="error_log">';
213
+ if ( file_exists( $errorpath ) ) {
214
+ echo "Now (auto-reload every 5 seconds):<br />[" . date( "d-M-Y H:i:s", time() ) . " UTC]<br /<br /><br />Errors (order by latest):";
215
+ $errors = file_get_contents( $errorpath );
216
+ $errors = explode( "\n", $errors );
217
+ $errors = array_reverse( $errors );
218
+ $errors = implode( "<br />", $errors );
219
+ echo $errors;
220
+ }
221
+ else {
222
+ echo "The PHP Error Logs cannot be found. Please ask your hosting service for it.";
223
+ }
224
+ echo "</div>";
225
+
226
+ }
227
+ else {
228
+
229
+ ?>
230
+ <?php $this->display_title(); ?>
231
+ <p>
232
+ <?php _e( 'Meow Apps is run by Jordy Meow, a photographer and software developer living in Japan (and taking <a target="_blank" href="http://offbeatjapan.org">a lot of photos</a>). Meow Apps is a suite of plugins focusing on photography, imaging, optimization and it teams up with the best players in the community (other themes and plugins developers). For more information, please check <a href="http://meowapps.com" target="_blank">Meow Apps</a>.', 'meowapps' )
233
+ ?>
234
+ </p>
235
+ <div class="meow-row">
236
+ <div class="meow-box meow-col meow-span_1_of_2 ">
237
+ <h3 class=""><span class="dashicons dashicons-camera"></span> UI Plugins </h3>
238
+ <ul class="">
239
+ <li><b>WP/LR Sync</b> <?php echo $this->check_install( 'wplr-sync' ) ?><br />
240
+ Bring synchronization from Lightroom to WordPress.</li>
241
+ <li><b>Meow Lightbox</b> <?php echo $this->check_install( 'meow-lightbox' ) ?><br />
242
+ Lightbox with EXIF information nicely displayed.</li>
243
+ <li><b>Meow Gallery</b> <?php echo $this->check_install( 'meow-gallery' ) ?><br />
244
+ Simple gallery to make your photos look better (Masonry and others).</li>
245
+ <li><b>Audio Story for Images</b> <?php echo $this->check_install( 'audio-story-images' ) ?><br />
246
+ Add audio to your images.</li>
247
+ </ul>
248
+ </div>
249
+ <div class="meow-box meow-col meow-span_1_of_2">
250
+ <h3 class=""><span class="dashicons dashicons-admin-tools"></span> System Plugins</h3>
251
+ <ul class="">
252
+ <li><b>Media File Renamer</b> <?php echo $this->check_install( 'media-file-renamer' ) ?><br />
253
+ Nicer filenames and better SEO, automatically.</li>
254
+ <li><b>Media Cleaner</b> <?php echo $this->check_install( 'media-cleaner' ) ?><br />
255
+ Detect the files which are not in use.</li>
256
+ <li><b>WP Retina 2x</b> <?php echo $this->check_install( 'wp-retina-2x' ) ?><br />
257
+ The famous plugin that adds Retina support.</li>
258
+ <li><b>WP Category Permalink</b> <?php echo $this->check_install( 'wp-category-permalink' ) ?><br />
259
+ Allows you to select a main category (or taxonomy) for nicer permalinks.</li>
260
+ </ul>
261
+ </div>
262
+ </div>
263
+
264
+ <div class="meow-row">
265
+ <div class="meow-box meow-col meow-span_2_of_3">
266
+ <h3><span class="dashicons dashicons-admin-tools"></span> Common</h3>
267
+ <div class="inside">
268
+ <form method="post" action="options.php">
269
+ <?php settings_fields( 'meowapps_common_settings' ); ?>
270
+ <?php do_settings_sections( 'meowapps_common_settings-menu' ); ?>
271
+ <?php submit_button(); ?>
272
+ </form>
273
+ </div>
274
+ </div>
275
+
276
+ <div class="meow-box meow-col meow-span_1_of_3">
277
+ <h3><span class="dashicons dashicons-admin-tools"></span> Debug</h3>
278
+ <div class="inside">
279
+ <ul>
280
+ <li><a href="?page=meowapps-main-menu&amp;tool=error_log">Display Error Log</a></li>
281
+ <li><a href="?page=meowapps-main-menu&amp;tool=phpinfo">Display PHP Info</a></li>
282
+ </ul>
283
+ </div>
284
+ </div>
285
+ </div>
286
+
287
+ <?php
288
+
289
+ }
290
+
291
+ echo "<br /><small style='color: lightgray;'>Meow Admin " . MeowApps_Admin::$admin_version . "</small></div>";
292
+ }
293
+
294
+ // HELPERS
295
+
296
+ static function size_shortname( $name ) {
297
+ $name = preg_split( '[_-]', $name );
298
+ $short = strtoupper( substr( $name[0], 0, 1 ) );
299
+ if ( count( $name ) > 1 )
300
+ $short .= strtoupper( substr( $name[1], 0, 1 ) );
301
+ return $short;
302
+ }
303
+
304
+ }
305
+
306
+ }
307
+
308
+ if ( file_exists( plugin_dir_path( __FILE__ ) . '/meowapps/admin.php' ) ) {
309
+ require( 'meowapps/admin.php' );
310
+ }
311
+
312
+ ?>
trunk/common/img/meowapps.png ADDED
Binary file
trunk/common/img/wpengine.png ADDED
Binary file
trunk/core.php ADDED
@@ -0,0 +1,1057 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Meow_WPMC_Core {
4
+
5
+ public $checkers = null;
6
+ public $admin = null;
7
+ public $debug = false;
8
+
9
+ public $last_analysis = null;
10
+ public $last_analysis_ids = null;
11
+
12
+ public function __construct( $admin ) {
13
+ $this->admin = $admin;
14
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
15
+ add_action( 'admin_enqueue_scripts', array( $this, 'wp_enqueue_scripts' ) );
16
+ add_action( 'admin_print_scripts', array( $this, 'admin_inline_js' ) );
17
+ add_action( 'wp_ajax_wpmc_scan', array( $this, 'wp_ajax_wpmc_scan' ) );
18
+ add_action( 'wp_ajax_wpmc_get_all_issues', array( $this, 'wp_ajax_wpmc_get_all_issues' ) );
19
+ add_action( 'wp_ajax_wpmc_get_all_deleted', array( $this, 'wp_ajax_wpmc_get_all_deleted' ) );
20
+ add_action( 'wp_ajax_wpmc_scan_do', array( $this, 'wp_ajax_wpmc_scan_do' ) );
21
+ add_action( 'wp_ajax_wpmc_delete_do', array( $this, 'wp_ajax_wpmc_delete_do' ) );
22
+ add_action( 'wp_ajax_wpmc_ignore_do', array( $this, 'wp_ajax_wpmc_ignore_do' ) );
23
+ add_action( 'wp_ajax_wpmc_recover_do', array( $this, 'wp_ajax_wpmc_recover_do' ) );
24
+ add_filter( 'media_row_actions', array( $this, 'media_row_actions' ), 10, 2 );
25
+
26
+ if ( is_admin() ) {
27
+ add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
28
+ }
29
+
30
+ // Checkers
31
+ require( 'wpmc_checkers.php' );
32
+ $this->checkers = new Meow_WPMC_Checkers( $this );
33
+ }
34
+
35
+ /*******************************************************************************
36
+ * METABOX FOR USAGE
37
+ ******************************************************************************/
38
+
39
+ function add_metabox() {
40
+ add_meta_box( 'mfrh_media_usage_box', 'Media Cleaner', array( $this, 'display_metabox' ), 'attachment', 'side', 'default' );
41
+ }
42
+
43
+ function display_metabox( $post ) {
44
+ if ( $this->wpmc_check_media( $post->ID ) ) {
45
+ if ( $this->last_analysis == "CONTENT" ) {
46
+ $result = array();
47
+ foreach ( $this->last_analysis_ids as $id )
48
+ array_push( $result, "<a href='post.php?post=$id&action=edit'>$id</a>" );
49
+ echo "Seems to be used in your content (" . implode( ', ', $result ) . ").";
50
+ }
51
+ else if ( $this->last_analysis == "BACKGROUND_OR_HEADER" ) {
52
+ echo "Seems to be used as a background or header.";
53
+ }
54
+ else if ( $this->last_analysis == "GALLERY" ) {
55
+ echo "Seems to be used in a gallery.";
56
+ }
57
+ else if ( $this->last_analysis == "META" ) {
58
+ echo "Seems to be used as a meta.";
59
+ }
60
+ else {
61
+ echo "Used: " . $this->last_analysis;
62
+ }
63
+ }
64
+ else {
65
+ echo "Doesn't seem to be used.";
66
+ }
67
+ }
68
+
69
+ /*******************************************************************************
70
+ * ASYNCHRONOUS AJAX FUNCTIONS
71
+ ******************************************************************************/
72
+
73
+ function wp_ajax_wpmc_delete_do () {
74
+ ob_start();
75
+ $data = $_POST['data'];
76
+ $success = 0;
77
+ foreach ( $data as $piece ) {
78
+ $success += ( $this->wpmc_delete( $piece ) ? 1 : 0 );
79
+ }
80
+ ob_end_clean();
81
+ echo json_encode(
82
+ array(
83
+ 'success' => true,
84
+ 'result' => array( 'data' => $data, 'success' => $success ),
85
+ 'message' => __( "Status unknown.", 'media-cleaner' )
86
+ )
87
+ );
88
+ die();
89
+ }
90
+
91
+ function wp_ajax_wpmc_ignore_do () {
92
+ ob_start();
93
+ $data = $_POST['data'];
94
+ $success = 0;
95
+ foreach ( $data as $piece ) {
96
+ $success += ( $this->wpmc_ignore( $piece ) ? 1 : 0 );
97
+ }
98
+ ob_end_clean();
99
+ echo json_encode(
100
+ array(
101
+ 'success' => true,
102
+ 'result' => array( 'data' => $data, 'success' => $success ),
103
+ 'message' => __( "Status unknown.", 'media-cleaner' )
104
+ )
105
+ );
106
+ die();
107
+ }
108
+
109
+ function wp_ajax_wpmc_recover_do () {
110
+ ob_start();
111
+ $data = $_POST['data'];
112
+ $success = 0;
113
+ foreach ( $data as $piece ) {
114
+ $success += ( $this->wpmc_recover( $piece ) ? 1 : 0 );
115
+ }
116
+ ob_end_clean();
117
+ echo json_encode(
118
+ array(
119
+ 'success' => true,
120
+ 'result' => array( 'data' => $data, 'success' => $success ),
121
+ 'message' => __( "Status unknown.", 'media-cleaner' )
122
+ )
123
+ );
124
+ die();
125
+ }
126
+
127
+ function wp_ajax_wpmc_scan_do () {
128
+ ob_start();
129
+ $type = $_POST['type'];
130
+ $data = $_POST['data'];
131
+ $success = 0;
132
+ foreach ( $data as $piece ) {
133
+ if ( $type == 'file' ) {
134
+ if ( $this->debug )
135
+ error_log( "Check File: {$piece}" );
136
+ $success += ( apply_filters( 'wpmc_check_file', true, $piece ) ? 1 : 0 );
137
+ if ( $this->debug )
138
+ error_log( "Success " . $success . "\n" );
139
+ } elseif ( $type == 'media' ) {
140
+ if ( $this->debug )
141
+ error_log( "Check Media: {$piece}" );
142
+ $success += ( $this->wpmc_check_media( $piece ) ? 1 : 0 );
143
+ if ( $this->debug )
144
+ error_log( "Success " . $success . "\n" );
145
+ }
146
+ }
147
+ ob_end_clean();
148
+ echo json_encode(
149
+ array(
150
+ 'success' => true,
151
+ 'result' => array( 'type' => $type, 'data' => $data, 'success' => $success ),
152
+ 'message' => __( "Items checked.", 'media-cleaner' )
153
+ )
154
+ );
155
+ die();
156
+ }
157
+
158
+ function wp_ajax_wpmc_get_all_deleted () {
159
+ global $wpdb;
160
+ $table_name = $wpdb->prefix . "wpmcleaner";
161
+ $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 1" );
162
+ echo json_encode(
163
+ array(
164
+ 'results' => array( 'ids' => $ids ),
165
+ 'success' => true,
166
+ 'message' => __( "List generated.", 'media-cleaner' )
167
+ )
168
+ );
169
+ die;
170
+ }
171
+
172
+ function wp_ajax_wpmc_get_all_issues () {
173
+ global $wpdb;
174
+ $isTrash = ( isset( $_POST['isTrash'] ) && $_POST['isTrash'] == 1 ) ? true : false;
175
+ $table_name = $wpdb->prefix . "wpmcleaner";
176
+ if ( $isTrash )
177
+ $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 1" );
178
+ else
179
+ $ids = $wpdb->get_col( "SELECT id FROM $table_name WHERE ignored = 0 AND deleted = 0" );
180
+ echo json_encode(
181
+ array(
182
+ 'results' => array( 'ids' => $ids ),
183
+ 'success' => true,
184
+ 'message' => __( "List generated.", 'media-cleaner' )
185
+ )
186
+ );
187
+ die;
188
+ }
189
+
190
+ function wpmc_get_galleries_images( $force = false ) {
191
+ if ( $force ) {
192
+ delete_transient( "wpmc_galleries_images" );
193
+ $galleries_images = null;
194
+ }
195
+ else {
196
+ $galleries_images = get_transient("wpmc_galleries_images");
197
+ }
198
+ if ( !$galleries_images ) {
199
+ global $wpdb;
200
+ $galleries_images = array();
201
+ $posts = $wpdb->get_col( "SELECT id FROM $wpdb->posts WHERE post_type != 'attachment' AND post_status != 'inherit'" );
202
+ foreach( $posts as $post ) {
203
+ $galleries = get_post_galleries_images( $post );
204
+ foreach( $galleries as $gallery ) {
205
+ foreach( $gallery as $image ) {
206
+ array_push( $galleries_images, $image );
207
+ }
208
+ }
209
+ }
210
+
211
+ $post_galleries = get_posts( array(
212
+ 'tax_query' => array(
213
+ array(
214
+ 'taxonomy' => 'post_format',
215
+ 'field' => 'slug',
216
+ 'terms' => array( 'post-format-gallery' ),
217
+ 'operator' => 'IN'
218
+ )
219
+ )
220
+ ) );
221
+
222
+ foreach( (array) $post_galleries as $gallery_post ) {
223
+ $arrImages = get_children( 'post_type=attachment&post_mime_type=image&post_parent=' . $gallery_post->ID );
224
+ if ( $arrImages ) {
225
+ foreach( (array) $arrImages as $image_post ) {
226
+ array_push( $galleries_images, $image_post->guid );
227
+ }
228
+ }
229
+ }
230
+ wp_reset_postdata();
231
+
232
+ set_transient( "wpmc_galleries_images", $galleries_images, 60 * 60 * 2 );
233
+ }
234
+ return $galleries_images;
235
+ }
236
+
237
+ function wp_ajax_wpmc_scan() {
238
+ global $wpdb;
239
+
240
+ $method = get_option( 'wpmc_method', 'media' );
241
+ if ( !$this->admin->is_registered() )
242
+ $method = 'media';
243
+ $path = isset( $_POST['path'] ) ? $_POST['path'] : null;
244
+ $limit = isset( $_POST['limit'] ) ? $_POST['limit'] : 0;
245
+ $limitsize = 100;
246
+
247
+ if ( ( $method == 'media' && empty( $limit ) ) || ( $method == 'files' && empty( $path ) ) ) {
248
+ // Reset and prepare all the Attachment IDs of all the galleries
249
+ $this->wpmc_reset_issues();
250
+ delete_transient( 'wpmc_posts_with_shortcode' );
251
+ $this->wpmc_get_galleries_images( true );
252
+ }
253
+
254
+ if ( $method == 'files' ) {
255
+ $output = apply_filters( 'wpmc_list_uploaded_files', array(
256
+ 'results' => array(), 'success' => false, 'message' => __( "Unavailable.", 'media-cleaner' )
257
+ ), $path );
258
+ echo json_encode( $output );
259
+ die();
260
+ }
261
+
262
+ if ( $method == 'media' ) {
263
+ // Prevent double scanning by removing filesystem entries that we have DB entries for
264
+ $results = $wpdb->get_col( $wpdb->prepare( "SELECT p.ID FROM $wpdb->posts p
265
+ WHERE p.post_status = 'inherit'
266
+ AND p.post_type = 'attachment'
267
+ LIMIT %d, %d", $limit, $limitsize
268
+ )
269
+ );
270
+ $finished = count( $results ) < $limitsize;
271
+ echo json_encode(
272
+ array(
273
+ 'results' => $results,
274
+ 'success' => true,
275
+ 'finished' => $finished,
276
+ 'limit' => $limit + $limitsize,
277
+ 'message' => __( "Medias retrieved.", 'media-cleaner' ) )
278
+ );
279
+ die();
280
+ }
281
+
282
+ // No task.
283
+ echo json_encode( array( 'success' => false, 'message' => __( "No task.", 'media-cleaner' ) ) );
284
+ die();
285
+ }
286
+
287
+ /**
288
+ *
289
+ * HELPERS
290
+ *
291
+ */
292
+
293
+ function wpmc_trashdir() {
294
+ $upload_folder = wp_upload_dir();
295
+ return trailingslashit( $upload_folder['basedir'] ) . 'wpmc-trash';
296
+ }
297
+
298
+ function wpmc_check_db() {
299
+ global $wpdb;
300
+ $tbl_m = $wpdb->prefix . 'wpmcleaner';
301
+ if ( !$wpdb->get_var( $wpdb->prepare( "SELECT COUNT(1) FROM information_schema.tables WHERE table_schema = '%s' AND table_name = '%s';", $wpdb->dbname, $tbl_m ) ) ) {
302
+ wpmc_activate();
303
+ }
304
+ }
305
+
306
+ /**
307
+ *
308
+ * DELETE / SCANNING / RESET
309
+ *
310
+ */
311
+
312
+ function wpmc_recover_file( $path ) {
313
+ $basedir = wp_upload_dir();
314
+ $originalPath = trailingslashit( $basedir['basedir'] ) . $path;
315
+ $trashPath = trailingslashit( $this->wpmc_trashdir() ) . $path;
316
+ $path_parts = pathinfo( $originalPath );
317
+ if ( !file_exists( $path_parts['dirname'] ) && !wp_mkdir_p( $path_parts['dirname'] ) ) {
318
+ die( 'Failed to create folder.' );
319
+ }
320
+ if ( !file_exists( $trashPath ) ) {
321
+ if ( $this->debug )
322
+ error_log( "The file $originalPath actually does not exist in the trash." );
323
+ return true;
324
+ }
325
+ if ( !rename( $trashPath, $originalPath ) ) {
326
+ die( 'Failed to move the file.' );
327
+ }
328
+ return true;
329
+ }
330
+
331
+ function wpmc_recover( $id ) {
332
+ global $wpdb;
333
+ $table_name = $wpdb->prefix . "wpmcleaner";
334
+ $issue = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $id ), OBJECT );
335
+ $issue->path = stripslashes( $issue->path );
336
+
337
+ // Files
338
+ if ( $issue->type == 0 ) {
339
+ $this->wpmc_recover_file( $issue->path );
340
+ $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 0 WHERE id = %d", $id ) );
341
+ return true;
342
+ }
343
+ // Media
344
+ else if ( $issue->type == 1 ) {
345
+
346
+ // Copy the main file back
347
+ $fullpath = get_attached_file( $issue->postId );
348
+ $mainfile = $this->wpmc_clean_uploaded_filename( $fullpath );
349
+ $baseUp = pathinfo( $mainfile );
350
+ $baseUp = $baseUp['dirname'];
351
+ $file = $this->wpmc_clean_uploaded_filename( $fullpath );
352
+ if ( !$this->wpmc_recover_file( $file ) )
353
+ error_log( "Could not recover $file." );
354
+
355
+ // If images, copy the other files as well
356
+ $meta = wp_get_attachment_metadata( $issue->postId );
357
+ $isImage = isset( $meta, $meta['width'], $meta['height'] );
358
+ $sizes = $this->wpmc_get_image_sizes();
359
+ if ( $isImage && isset( $meta['sizes'] ) ) {
360
+ foreach ( $meta['sizes'] as $name => $attr ) {
361
+ if ( isset( $attr['file'] ) ) {
362
+ $filepath = wp_upload_dir();
363
+ $filepath = $filepath['basedir'];
364
+ $filepath = trailingslashit( $filepath ) . trailingslashit( $baseUp ) . $attr['file'];
365
+ $file = $this->wpmc_clean_uploaded_filename( $filepath );
366
+ if ( !$this->wpmc_recover_file( $file ) )
367
+ error_log( "Could not recover $file." );
368
+ }
369
+ }
370
+ }
371
+ if ( !wp_untrash_post( $issue->postId ) )
372
+ die( "Failed to untrash Media {$issue->postId}." );
373
+ $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 0 WHERE id = %d", $id ) );
374
+ return true;
375
+ }
376
+ }
377
+
378
+ function wpmc_trash_file( $fileIssuePath ) {
379
+ global $wpdb;
380
+ $basedir = wp_upload_dir();
381
+ $originalPath = trailingslashit( $basedir['basedir'] ) . $fileIssuePath;
382
+ $trashPath = trailingslashit( $this->wpmc_trashdir() ) . $fileIssuePath;
383
+ $path_parts = pathinfo( $trashPath );
384
+
385
+ try {
386
+ if ( !file_exists( $path_parts['dirname'] ) && !wp_mkdir_p( $path_parts['dirname'] ) ) {
387
+ return false;
388
+ }
389
+ // Rename the file (move). 'is_dir' is just there for security (no way we should move a whole directory)
390
+ if ( is_dir( $originalPath ) ) {
391
+ error_log( "Attempted to delete a directory instead of a file ($originalPath). Can't do that." );
392
+ return false;
393
+ }
394
+ if ( !file_exists( $originalPath ) ) {
395
+ if ( $this->debug )
396
+ error_log( "The file $originalPath actually does not exist." );
397
+ return true;
398
+ }
399
+ if ( !rename( $originalPath, $trashPath ) ) {
400
+ return false;
401
+ }
402
+ }
403
+ catch ( Exception $e ) {
404
+ return false;
405
+ }
406
+ $this->wpmc_clean_dir( dirname( $originalPath ) );
407
+ return true;
408
+ }
409
+
410
+ function wpmc_ignore( $id ) {
411
+ global $wpdb;
412
+ $table_name = $wpdb->prefix . "wpmcleaner";
413
+ $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET ignored = 1 WHERE id = %d", $id ) );
414
+ return true;
415
+ }
416
+
417
+ function wpmc_endsWith( $haystack, $needle )
418
+ {
419
+ $length = strlen( $needle );
420
+ if ( $length == 0 )
421
+ return true;
422
+ return ( substr( $haystack, -$length ) === $needle );
423
+ }
424
+
425
+ function wpmc_clean_dir( $dir ) {
426
+ if ( !file_exists( $dir ) )
427
+ return;
428
+ else if ( $this->wpmc_endsWith( $dir, 'uploads' ) )
429
+ return;
430
+ $found = array_diff( scandir( $dir ), array( '.', '..' ) );
431
+ if ( count( $found ) < 1 ) {
432
+ if ( rmdir( $dir ) ) {
433
+ $this->wpmc_clean_dir( dirname( $dir ) );
434
+ }
435
+ }
436
+ }
437
+
438
+ function wpmc_delete( $id ) {
439
+ global $wpdb;
440
+ $table_name = $wpdb->prefix . "wpmcleaner";
441
+ $issue = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $id ), OBJECT );
442
+ $regex = "^(.*)(\\s\\(\\+.*)$";
443
+ $issue->path = preg_replace('/'.$regex.'/i', '$1', $issue->path); // remove " (+ 6 files)" from path
444
+
445
+ // Make sure there isn't a media DB entry
446
+ if ( $issue->type == 0 ) {
447
+ $attachmentid = $this->wpmc_find_attachment_id_by_file( $issue->path );
448
+ if ( $attachmentid ) {
449
+ error_log( "File Cleaner: Issue listed as filesystem but Media {$attachmentid} exists." );
450
+ }
451
+ }
452
+
453
+ if ( $issue->type == 0 ) {
454
+
455
+ if ( $issue->deleted == 0 ) {
456
+ // Move file to the trash
457
+ if ( $this->wpmc_trash_file( $issue->path ) )
458
+ $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 1 WHERE id = %d", $id ) );
459
+ return true;
460
+ }
461
+ else {
462
+ // Delete file from the trash
463
+ $trashPath = trailingslashit( $this->wpmc_trashdir() ) . $issue->path;
464
+ if ( !unlink( $trashPath ) )
465
+ error_log( 'Failed to delete the file.' );
466
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE id = %d", $id ) );
467
+ $this->wpmc_clean_dir( dirname( $trashPath ) );
468
+ return true;
469
+ }
470
+ }
471
+
472
+ if ( $issue->type == 1 ) {
473
+ if ( $issue->deleted == 0 && MEDIA_TRASH ) {
474
+ // Move Media to trash
475
+ // Let's copy the images to the trash so that it can be recovered.
476
+ $fullpath = get_attached_file( $issue->postId );
477
+ $mainfile = $this->wpmc_clean_uploaded_filename( $fullpath );
478
+ $baseUp = pathinfo( $mainfile );
479
+ $baseUp = $baseUp['dirname'];
480
+ $file = $this->wpmc_clean_uploaded_filename( $fullpath );
481
+ if ( !$this->wpmc_trash_file( $file ) )
482
+ error_log( "Could not trash $file." );
483
+
484
+ // If images, check the other files as well
485
+ $meta = wp_get_attachment_metadata( $issue->postId );
486
+ $isImage = isset( $meta, $meta['width'], $meta['height'] );
487
+ $sizes = $this->wpmc_get_image_sizes();
488
+ if ( $isImage && isset( $meta['sizes'] ) ) {
489
+ foreach ( $meta['sizes'] as $name => $attr ) {
490
+ if ( isset( $attr['file'] ) ) {
491
+ $filepath = wp_upload_dir();
492
+ $filepath = $filepath['basedir'];
493
+ $filepath = trailingslashit( $filepath ) . trailingslashit( $baseUp ) . $attr['file'];
494
+ $file = $this->wpmc_clean_uploaded_filename( $filepath );
495
+ if ( !$this->wpmc_trash_file( $file ) )
496
+ error_log( "Could not trash $file." );
497
+ }
498
+ }
499
+ }
500
+ wp_delete_attachment( $issue->postId, false );
501
+ $wpdb->query( $wpdb->prepare( "UPDATE $table_name SET deleted = 1 WHERE id = %d", $id ) );
502
+ return true;
503
+ }
504
+ else {
505
+ // Trash Media definitely by recovering it (to be like a normal Media) and remove it through the
506
+ // standard WordPress workflow
507
+ if ( MEDIA_TRASH )
508
+ $this->wpmc_recover( $id );
509
+ wp_delete_attachment( $issue->postId, true );
510
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE id = %d", $id ) );
511
+ return true;
512
+ }
513
+ }
514
+ return false;
515
+ }
516
+
517
+ /**
518
+ *
519
+ * SCANNING / RESET
520
+ *
521
+ */
522
+
523
+ function wpmc_check_is_ignore( $file ) {
524
+ global $wpdb;
525
+ $table_name = $wpdb->prefix . "wpmcleaner";
526
+ $count = $wpdb->get_var( "SELECT COUNT(*)
527
+ FROM $table_name
528
+ WHERE ignored = 1
529
+ AND path LIKE '%". esc_sql( $wpdb->esc_like( $file ) ) . "%'" );
530
+ if ( $this->debug && $count > 0 )
531
+ error_log("{$file} found in IGNORE");
532
+ return ($count > 0);
533
+ }
534
+
535
+ function wpmc_find_attachment_id_by_file ($file) {
536
+ global $wpdb;
537
+ $postmeta_table_name = $wpdb->prefix . 'postmeta';
538
+ $file = $this->wpmc_clean_uploaded_filename( $file );
539
+ $sql = $wpdb->prepare( "SELECT post_id
540
+ FROM {$postmeta_table_name}
541
+ WHERE meta_key = '_wp_attached_file'
542
+ AND meta_value = %s", $file
543
+ );
544
+ $ret = $wpdb->get_var( $sql );
545
+ if ( $this->debug && empty( $ret ) )
546
+ error_log( "File $file not found as _wp_attached_file (Library)." );
547
+ else if ( $this->debug ) {
548
+ error_log( "File $file found as Media $ret." );
549
+ }
550
+ return $ret;
551
+ }
552
+
553
+ function wpmc_get_image_sizes() {
554
+ $sizes = array();
555
+ global $_wp_additional_image_sizes;
556
+ foreach ( get_intermediate_image_sizes() as $s ) {
557
+ $crop = false;
558
+ if ( isset( $_wp_additional_image_sizes[$s] ) ) {
559
+ $width = intval( $_wp_additional_image_sizes[$s]['width'] );
560
+ $height = intval( $_wp_additional_image_sizes[$s]['height'] );
561
+ $crop = $_wp_additional_image_sizes[$s]['crop'];
562
+ } else {
563
+ $width = get_option( $s.'_size_w' );
564
+ $height = get_option( $s.'_size_h' );
565
+ $crop = get_option( $s.'_crop' );
566
+ }
567
+ $sizes[$s] = array( 'width' => $width, 'height' => $height, 'crop' => $crop );
568
+ }
569
+ return $sizes;
570
+ }
571
+
572
+
573
+ // From a fullpath to the shortened and cleaned path (for example '2013/02/file.png')
574
+ function wpmc_clean_uploaded_filename( $fullpath ) {
575
+ $upload_folder = wp_upload_dir();
576
+ $basedir = $upload_folder['basedir'];
577
+ $file = str_replace( $basedir, '', $fullpath );
578
+ $file = trim( $file, "/" );
579
+ return $file;
580
+ }
581
+
582
+ function wpmc_check_media( $attachmentId ) {
583
+
584
+ $this->last_analysis = "N/A";
585
+
586
+ // Is it an image?
587
+ $meta = wp_get_attachment_metadata( $attachmentId );
588
+ $isImage = isset( $meta, $meta['width'], $meta['height'] );
589
+
590
+ // Get the main file
591
+ global $wpdb;
592
+ $fullpath = get_attached_file( $attachmentId );
593
+ $mainfile = $this->wpmc_clean_uploaded_filename( $fullpath );
594
+ $baseUp = pathinfo( $mainfile );
595
+ $baseUp = $baseUp['dirname'];
596
+ $size = 0;
597
+ $countfiles = 0;
598
+ $issue = 'NO_CONTENT';
599
+ if ( file_exists( $fullpath ) ) {
600
+ $size = filesize( $fullpath );
601
+
602
+ // ANALYSIS
603
+ $this->last_analysis = "NONE";
604
+ if ( $this->wpmc_check_is_ignore( $mainfile ) ) {
605
+ $this->last_analysis = "IGNORED";
606
+ return true;
607
+ }
608
+ if ( $this->checkers->has_background_or_header( $mainfile ) ) {
609
+ $this->last_analysis = "BACKGROUND_OR_HEADER";
610
+ return true;
611
+ }
612
+ if ( $this->checkers->has_content( $mainfile ) ) {
613
+ $this->last_analysis = "CONTENT";
614
+ return true;
615
+ }
616
+ if ( $this->checkers->wpmc_check_in_gallery( $mainfile ) ) {
617
+ $this->last_analysis = "GALLERY";
618
+ return true;
619
+ }
620
+ if ( $this->checkers->has_meta( $mainfile, $attachmentId ) ) {
621
+ $this->last_analysis = "META";
622
+ return true;
623
+ }
624
+
625
+ // If images, check the other files as well
626
+ $countfiles = 0;
627
+ $sizes = $this->wpmc_get_image_sizes();
628
+ if ( $isImage && isset( $meta['sizes'] ) ) {
629
+ foreach ( $meta['sizes'] as $name => $attr ) {
630
+ if ( isset( $attr['file'] ) ) {
631
+ $filepath = wp_upload_dir();
632
+ $filepath = $filepath['basedir'];
633
+ $filepath = trailingslashit( $filepath ) . trailingslashit( $baseUp ) . $attr['file'];
634
+ if ( file_exists( $filepath ) ) {
635
+ $size += filesize( $filepath );
636
+ }
637
+ $file = $this->wpmc_clean_uploaded_filename( $attr['file'] );
638
+ $countfiles++;
639
+ if ( $this->debug )
640
+ error_log("checking MEDIA-IMAGE {$filepath}");
641
+
642
+ // ANALYSIS
643
+ if ( $this->checkers->wpmc_check_in_gallery( $filepath ) ) {
644
+ $this->last_analysis = "GALLERY";
645
+ return true;
646
+ }
647
+ if ( $this->checkers->has_background_or_header( $filepath ) ) {
648
+ $this->last_analysis = "BACKGROUND_OR_HEADER";
649
+ return true;
650
+ }
651
+ if ( $this->checkers->has_meta( $filepath, $attachmentId ) ) {
652
+ $this->last_analysis = "META";
653
+ return true;
654
+ }
655
+
656
+ }
657
+ }
658
+ }
659
+ } else {
660
+ $issue = 'ORPHAN_MEDIA';
661
+ }
662
+
663
+ $table_name = $wpdb->prefix . "wpmcleaner";
664
+ $wpdb->insert( $table_name,
665
+ array(
666
+ 'time' => current_time('mysql'),
667
+ 'type' => 1,
668
+ 'size' => $size,
669
+ 'path' => $mainfile . ( $countfiles > 0 ? ( " (+ " . $countfiles . " files)" ) : "" ),
670
+ 'postId' => $attachmentId,
671
+ 'issue' => $issue
672
+ )
673
+ );
674
+ return false;
675
+ }
676
+
677
+ // Delete all issues
678
+ function wpmc_reset_issues( $includingIgnored = false ) {
679
+ global $wpdb;
680
+ $table_name = $wpdb->prefix . "wpmcleaner";
681
+ if ( $includingIgnored ) {
682
+ $wpdb->query( "DELETE FROM $table_name WHERE deleted = 0" );
683
+ }
684
+ else {
685
+ $wpdb->query( "DELETE FROM $table_name WHERE ignored = 0 AND deleted = 0" );
686
+ }
687
+ }
688
+
689
+ /**
690
+ *
691
+ * DASHBOARD
692
+ *
693
+ */
694
+
695
+ function admin_inline_js() {
696
+ echo "<script type='text/javascript'>\n";
697
+ echo 'var wpmc_cfg = { isPro: ' . ( $this->admin->is_registered() ? '1' : '0') . ', scanFiles: ' . ( ( get_option( 'wpmc_method', 'media' ) == 'files' && $this->admin->is_registered() ) ? '1' : '0' ) . ', scanMedia: ' . ( get_option( 'wpmc_method', 'media' ) == 'media' ? '1' : '0' ) . ' };';
698
+ echo "\n</script>";
699
+ }
700
+
701
+ function echo_issue( $issue ) {
702
+ if ( $issue == 'NO_CONTENT' ) {
703
+ _e( "Seems not in use.", 'media-cleaner' );
704
+ }
705
+ else if ( $issue == 'NO_MEDIA' ) {
706
+ _e( "Not in Media Library.", 'media-cleaner' );
707
+ }
708
+ else if ( $issue == 'ORPHAN_RETINA' ) {
709
+ _e( "Orphan retina.", 'media-cleaner' );
710
+ }
711
+ else if ( $issue == 'ORPHAN_MEDIA' ) {
712
+ _e( "File not found.", 'media-cleaner' );
713
+ }
714
+ else {
715
+ echo $issue;
716
+ }
717
+ }
718
+
719
+ function media_row_actions( $actions, $post ) {
720
+ global $current_screen;
721
+ if ( 'upload' != $current_screen->id )
722
+ return $actions;
723
+ global $wpdb;
724
+ $table_name = $wpdb->prefix . "wpmcleaner";
725
+ $res = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE postId = %d", $post->ID ) );
726
+ if ( !empty( $res ) && isset( $actions['delete'] ) )
727
+ $actions['delete'] = "<a href='?page=media-cleaner&view=deleted'>Delete with Media Cleaner</a>";
728
+ if ( !empty( $res ) && isset( $actions['trash'] ) )
729
+ $actions['trash'] = "<a href='?page=media-cleaner'>Trash with Media Cleaner</a>";
730
+ if ( !empty( $res ) && isset( $actions['untrash'] ) ) {
731
+ $actions['untrash'] = "<a href='?page=media-cleaner&view=deleted'>Restore with Media Cleaner</a>";
732
+ }
733
+ return $actions;
734
+ }
735
+
736
+ function wpmc_screen() {
737
+ global $wplr;
738
+ $this->wpmc_check_db();
739
+ ?>
740
+ <div class='wrap'>
741
+
742
+ <?php
743
+ echo $this->admin->display_title( "Media Cleaner" );
744
+ global $wpdb;
745
+ $posts_per_page = get_user_meta( get_current_user_id(), 'upload_per_page', true );
746
+ if ( empty( $posts_per_page ) )
747
+ $posts_per_page = 20;
748
+ $view = isset ( $_GET[ 'view' ] ) ? sanitize_text_field( $_GET[ 'view' ] ) : "issues";
749
+ $paged = isset ( $_GET[ 'paged' ] ) ? sanitize_text_field( $_GET[ 'paged' ] ) : 1;
750
+ $reset = isset ( $_GET[ 'reset' ] ) ? $_GET[ 'reset' ] : 0;
751
+ if ( $reset ) {
752
+ wpmc_reset();
753
+ }
754
+ $s = isset ( $_GET[ 's' ] ) ? sanitize_text_field( $_GET[ 's' ] ) : null;
755
+ $table_name = $wpdb->prefix . "wpmcleaner";
756
+ $issues_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE ignored = 0 AND deleted = 0" );
757
+ $total_size = $wpdb->get_var( "SELECT SUM(size) FROM $table_name WHERE ignored = 0 AND deleted = 0" );
758
+ $trash_total_size = $wpdb->get_var( "SELECT SUM(size) FROM $table_name WHERE ignored = 0 AND deleted = 1" );
759
+ $ignored_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE ignored = 1" );
760
+ $deleted_count = $wpdb->get_var( "SELECT COUNT(*) FROM $table_name WHERE deleted = 1" );
761
+
762
+ if ( $view == 'deleted' ) {
763
+ $items_count = $deleted_count;
764
+ $items = $wpdb->get_results( $wpdb->prepare( "SELECT id, type, postId, path, size, ignored, deleted, issue
765
+ FROM $table_name WHERE ignored = 0 AND deleted = 1 AND path LIKE %s
766
+ ORDER BY time
767
+ DESC LIMIT %d, %d", '%' . $s . '%', ( $paged - 1 ) * $posts_per_page, $posts_per_page ), OBJECT );
768
+ }
769
+ else if ( $view == 'ignored' ) {
770
+ $items_count = $ignored_count;
771
+ $items = $wpdb->get_results( $wpdb->prepare( "SELECT id, type, postId, path, size, ignored, deleted, issue
772
+ FROM $table_name
773
+ WHERE ignored = 1 AND deleted = 0 AND path LIKE %s
774
+ ORDER BY time
775
+ DESC LIMIT %d, %d", '%' . $s . '%', ( $paged - 1 ) * $posts_per_page, $posts_per_page ), OBJECT );
776
+ }
777
+ else {
778
+ $items_count = $issues_count;
779
+ $items = $wpdb->get_results( $wpdb->prepare( "SELECT id, type, postId, path, size, ignored, deleted, issue
780
+ FROM $table_name
781
+ WHERE ignored = 0 AND deleted = 0 AND path LIKE %s
782
+ ORDER BY time
783
+ DESC LIMIT %d, %d", '%' . $s . '%', ( $paged - 1 ) * $posts_per_page, $posts_per_page ), OBJECT );
784
+ }
785
+ ?>
786
+
787
+ <style>
788
+ #wpmc-pages {
789
+ float: right;
790
+ position: relative;
791
+ top: 12px;
792
+ }
793
+
794
+ #wpmc-pages a {
795
+ text-decoration: none;
796
+ border: 1px solid black;
797
+ padding: 2px 5px;
798
+ border-radius: 4px;
799
+ background: #E9E9E9;
800
+ color: lightslategrey;
801
+ border-color: #BEBEBE;
802
+ }
803
+
804
+ #wpmc-pages .current {
805
+ font-weight: bold;
806
+ }
807
+ </style>
808
+
809
+ <div style='margin-top: 0px; background: #FFF; padding: 5px; border-radius: 4px; height: 28px; box-shadow: 0px 0px 6px #C2C2C2;'>
810
+
811
+ <!-- SCAN -->
812
+ <?php if ( $view != 'deleted' ) { ?>
813
+ <a id='wpmc_scan' onclick='wpmc_scan()' class='button-primary' style='float: left;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-search"></span><?php _e("Scan", 'media-cleaner'); ?></a>
814
+ <?php } ?>
815
+
816
+ <!-- DELETE SELECTED -->
817
+ <a id='wpmc_delete' onclick='wpmc_delete()' class='button' style='float: left; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-no"></span><?php _e("Delete", 'media-cleaner'); ?></a>
818
+ <?php if ( $view == 'deleted' ) { ?>
819
+ <a id='wpmc_recover' onclick='wpmc_recover()' class='button-secondary' style='float: left; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-controls-repeat"></span><?php _e( "Recover", 'media-cleaner' ); ?></a>
820
+ <?php } ?>
821
+
822
+ <!-- IGNORE SELECTED -->
823
+ <a id='wpmc_ignore' onclick='wpmc_ignore()' class='button' style='float: left; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-yes"></span><?php _e("Ignore", 'media-cleaner'); ?></a>
824
+
825
+ <!-- RESET -->
826
+ <?php if ( $view != 'deleted' ) { ?>
827
+ <a id='wpmc_reset' href='?page=media-cleaner&reset=1' class='button-primary' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-sos"></span><?php _e("Reset", 'media-cleaner'); ?></a>
828
+ <?php } ?>
829
+
830
+ <!-- DELETE ALL -->
831
+ <?php if ( $view == 'deleted' ) { ?>
832
+ <a id='wpmc_recover_all' onclick='wpmc_recover_all()' class='button-primary' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-controls-repeat"></span><?php _e("Recover all", 'media-cleaner'); ?></a>
833
+ <a id='wpmc_delete_all' onclick='wpmc_delete_all(true)' class='button button-red' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-trash"></span><?php _e("Empty trash", 'media-cleaner'); ?></a>
834
+ <?php } else { ?>
835
+ <a id='wpmc_delete_all' onclick='wpmc_delete_all()' class='button button-red' style='float: right; margin-left: 5px;'><span style="top: 3px; position: relative; left: -5px;" class="dashicons dashicons-trash"></span><?php _e("Delete all", 'media-cleaner'); ?></a>
836
+ <?php } ?>
837
+
838
+ <form id="posts-filter" action="upload.php" method="get" style='float: right;'>
839
+ <p class="search-box" style='margin-left: 5px; float: left;'>
840
+ <input type="search" name="s" style="width: 120px;" value="<?php echo $s ? $s : ""; ?>">
841
+ <input type="hidden" name="page" value="media-cleaner">
842
+ <input type="hidden" name="view" value="<?php echo $view; ?>">
843
+ <input type="hidden" name="paged" value="<?php echo $paged; ?>">
844
+ <input type="submit" class="button" value="<?php _e( 'Search', 'media-cleaner' ) ?>"><span style='border-right: #A2A2A2 solid 1px; margin-left: 5px; margin-right: 3px;'>&nbsp;</span>
845
+ </p>
846
+ </form>
847
+
848
+ <!-- PROGRESS -->
849
+ <span style='margin-left: 12px; font-size: 15px; top: 5px; position: relative; color: #747474;' id='wpmc_progression'></span>
850
+
851
+ </div>
852
+
853
+ <p>
854
+ <?php
855
+ $method = get_option( 'wpmc_method', 'media' );
856
+ if ( !$this->admin->is_registered() )
857
+ $method = 'media';
858
+
859
+ $hide_warning = get_option( 'wpmc_hide_warning', false );
860
+
861
+ if ( !$hide_warning ) {
862
+ _e( "<div class='notice notice-error'><p><b style='color: red;'>Important.</b> <b>Backup your DB and your /uploads directory before using Media Cleaner. </b> The deleted files will be temporarily moved to the <b>uploads/wpmc-trash</b> directory. After testing your website, you can check the <a href='?page=media-cleaner&s&view=deleted'>trash</a> to either empty it or recover the media and files. The Media Cleaner does its best to be safe to use. However, WordPress being a very dynamic and pluggable system, it is impossible to predict all the situations in which your files are used. <b style='color: red;'>Again, please backup!</b> <br /><br /><b style='color: red;'>Be thoughtful.</b> Don't blame Media Cleaner if it deleted too many or not enough of your files. It makes cleaning possible and this task is only possible this way; don't post a bad review because it broke your install. <b>If you have a proper backup, there is no risk</b>. Sorry for the lengthy message, but better be safe than sorry. You can disable this big warning in the options if you have a Pro license. Make sure you read this warning twice. Media Cleaner is awesome and always getting better so I hope you will enjoy it. Thank you :)</p></div>", 'media-cleaner' );
863
+ }
864
+
865
+ if ( !MEDIA_TRASH ) {
866
+ _e( "<div class='notice notice-warning'><p>The trash for the Media Library is disabled. Any media removed by the plugin will be <b>permanently deleted</b>. To enable it, modify your wp-config.php file and add this line:<br /><b>define( 'MEDIA_TRASH', true );</b></p></div>" );
867
+ }
868
+
869
+ if ( !$this->admin->is_registered() ) {
870
+ echo "<div class='notice notice-info'><p>";
871
+ _e( "<b>This version is not Pro.</b> This plugin is a lot of work so please consider in getting the Pro version in order to receive support and to help the plugin to evolve. Also, the Pro version will also give you the option <b>to scan the <u>physical files</u> in your /uploads folder</b>. You can <a target='_blank' href='http://meowapps.com/media-cleaner'>get a serial for the Pro version here</a></b>.", 'media-cleaner' );
872
+ echo "</p></div>";
873
+ }
874
+
875
+ $anychecks = get_option(' wpmc_posts', false ) || get_option(' wpmc_galleries', false ) || get_option(' wpmc_postmeta', false );
876
+ $check_library = get_option(' wpmc_media_library', false );
877
+
878
+ if ( $method == 'media' ) {
879
+ if ( !$anychecks )
880
+ _e( "<div class='error'><p>Media Cleaner will analyze your Media Library. There is however <b>nothing marked to be check</b> in the Settings.</p></div>", 'media-cleaner' );
881
+ else
882
+ _e( "<div class='notice notice-success'><p>Media Cleaner will analyze your Media Library.</p></div>", 'media-cleaner' );
883
+ }
884
+ else if ( $method == 'files' ) {
885
+ if ( !$anychecks && !$check_library )
886
+ _e( "<div class='error'><p>Media Cleaner will analyze your Filesystem. There is however <b>nothing marked to be check</b> in the Settings.</p></div>", 'media-cleaner' );
887
+ else
888
+ _e( "<div class='notice notice-success'><p>Media Cleaner will analyze your Filesystem.</p></div>", 'media-cleaner' );
889
+ }
890
+
891
+ echo sprintf( __( 'There are <b>%s issue(s)</b> with your files, accounting for <b>%s MB</b>. Your trash contains <b>%s MB.</b>', 'media-cleaner' ), number_format( $issues_count, 0 ), number_format( $total_size / 1000000, 2 ), number_format( $trash_total_size / 1000000, 2 ) );
892
+ ?>
893
+ </p>
894
+
895
+ <div id='wpmc-pages'>
896
+ <?php
897
+ echo paginate_links(array(
898
+ 'base' => '?page=media-cleaner&s=' . urlencode($s) . '&view=' . $view . '%_%',
899
+ 'current' => $paged,
900
+ 'format' => '&paged=%#%',
901
+ 'total' => ceil( $items_count / $posts_per_page ),
902
+ 'prev_next' => false
903
+ ));
904
+ ?>
905
+ </div>
906
+
907
+ <ul class="subsubsub">
908
+ <li class="all"><a <?php if ( $view == 'issues' ) echo "class='current'"; ?> href='?page=media-cleaner&s=<?php echo $s; ?>&view=issues'><?php _e( "Issues", 'media-cleaner' ); ?></a><span class="count">(<?php echo $issues_count; ?>)</span></li> |
909
+ <li class="all"><a <?php if ( $view == 'ignored' ) echo "class='current'"; ?> href='?page=media-cleaner&s=<?php echo $s; ?>&view=ignored'><?php _e( "Ignored", 'media-cleaner' ); ?></a><span class="count">(<?php echo $ignored_count; ?>)</span></li> |
910
+ <li class="all"><a <?php if ( $view == 'deleted' ) echo "class='current'"; ?> href='?page=media-cleaner&s=<?php echo $s; ?>&view=deleted'><?php _e( "Trash", 'media-cleaner' ); ?></a><span class="count">(<?php echo $deleted_count; ?>)</span></li>
911
+ </ul>
912
+
913
+ <table id='wpmc-table' class='wp-list-table widefat fixed media'>
914
+
915
+ <thead>
916
+ <tr>
917
+ <th scope="col" id="cb" class="manage-column column-cb check-column"><input id="wpmc-cb-select-all" type="checkbox"></th>
918
+ <?php if ( !get_option( 'wpmc_hide_thumbnails', false ) ): ?>
919
+ <th style='width: 64px;'><?php _e( 'Thumb', 'media-cleaner' ) ?></th>
920
+ <?php endif; ?>
921
+ <th style='width: 50px;'><?php _e( 'Type', 'media-cleaner' ) ?></th>
922
+ <th style='width: 80px;'><?php _e( 'Origin', 'media-cleaner' ) ?></th>
923
+
924
+ <?php if ( !empty( $wplr ) ): ?>
925
+ <th style='width: 70px;'><?php _e( 'LR ID', 'media-cleaner' ) ?></th>
926
+ <?php endif; ?>
927
+
928
+ <th><?php _e( 'Path', 'media-cleaner' ) ?></th>
929
+ <th style='width: 220px;'><?php _e( 'Issue', 'media-cleaner' ) ?></th>
930
+ <th style='width: 80px; text-align: right;'><?php _e( 'Size', 'media-cleaner' ) ?></th>
931
+ </tr>
932
+ </thead>
933
+
934
+ <tbody>
935
+ <?php
936
+ foreach ( $items as $issue ) {
937
+ $regex = "^(.*)(\\s\\(\\+.*)$";
938
+ $issue->path = preg_replace( '/' .$regex . '/i', '$1', $issue->path );
939
+ ?>
940
+ <tr>
941
+ <td><input type="checkbox" name="id" value="<?php echo $issue->id ?>"></td>
942
+ <?php if ( !get_option( 'wpmc_hide_thumbnails', false ) ): ?>
943
+ <td>
944
+ <?php
945
+ if ( $issue->deleted == 0 ) {
946
+ if ( $issue ->type == 0 ) {
947
+ // FILE
948
+ $upload_dir = wp_upload_dir();
949
+ $url = htmlspecialchars( $upload_dir['baseurl'] . '/' . $issue->path, ENT_QUOTES );
950
+ echo "<a target='_blank' href='" . $url .
951
+ "'><img style='max-width: 48px; max-height: 48px;' src='" . $url . "' /></a>";
952
+ }
953
+ else {
954
+ // MEDIA
955
+ $attachmentsrc = wp_get_attachment_image_src( $issue->postId, 'thumbnail' );
956
+ $attachmentsrc_clean = htmlspecialchars( $attachmentsrc[0], ENT_QUOTES );
957
+ echo "<a target='_blank' href='" . $attachmentsrc_clean .
958
+ "'><img style='max-width: 48px; max-height: 48px;' src='" .
959
+ $attachmentsrc_clean . "' />";
960
+ }
961
+ }
962
+ if ( $issue->deleted == 1 ) {
963
+ $upload_dir = wp_upload_dir();
964
+ $url = htmlspecialchars( $upload_dir['baseurl'] . '/wpmc-trash/' . $issue->path, ENT_QUOTES );
965
+ echo "<a target='_blank' href='" . $url .
966
+ "'><img style='max-width: 48px; max-height: 48px;' src='" . $url . "' /></a>";
967
+ }
968
+ ?>
969
+ </td>
970
+ <?php endif; ?>
971
+ <td><?php echo $issue->type == 0 ? 'FILE' : 'MEDIA'; ?></td>
972
+ <td><?php echo $issue->type == 0 ? 'Filesystem' : ("<a href='post.php?post=" .
973
+ $issue->postId . "&action=edit'>ID " . $issue->postId . "</a>"); ?></td>
974
+ <?php if ( !empty( $wplr ) ) { $info = $wplr->get_sync_info( $issue->postId ); ?>
975
+ <td style='width: 70px;'><?php echo ( !empty( $info ) && $info->lr_id ? $info->lr_id : "" ); ?></td>
976
+ <?php } ?>
977
+ <td><?php echo stripslashes( $issue->path ); ?></td>
978
+ <td><?php $this->echo_issue( $issue->issue ); ?></td>
979
+ <td style='text-align: right;'><?php echo number_format( $issue->size / 1000, 2 ); ?> KB</td>
980
+ </tr>
981
+ <?php } ?>
982
+ </tbody>
983
+
984
+ <tfoot>
985
+ <tr><th></th>
986
+ <?php if ( !get_option( 'hide_thumbnails', false ) ): ?>
987
+ <th></th>
988
+ <?php endif; ?>
989
+ <th><?php _e( 'Type', 'media-cleaner' ) ?></th><th><?php _e( 'Origin', 'media-cleaner' ) ?></th>
990
+ <?php if ( !empty( $wplr ) ): ?>
991
+ <th style='width: 70px;'><?php _e( 'LR ID', 'media-cleaner' ) ?></th>
992
+ <?php endif; ?>
993
+ <th><?php _e( 'Path', 'media-cleaner' ) ?></th><th><?php _e( 'Issue', 'media-cleaner' ) ?></th><th style='width: 80px; text-align: right;'><?php _e( 'Size', 'media-cleaner' ) ?></th></tr>
994
+ </tfoot>
995
+
996
+ </table>
997
+ </wrap>
998
+
999
+ <?php
1000
+ }
1001
+
1002
+ function admin_menu() {
1003
+ //load_plugin_textdomain( 'media-cleaner', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
1004
+ add_media_page( 'Media Cleaner', 'Cleaner', 'manage_options', 'media-cleaner', array( $this, 'wpmc_screen' ) );
1005
+ }
1006
+
1007
+ function wp_enqueue_scripts () {
1008
+ wp_enqueue_style( 'media-cleaner-css', plugins_url( '/media-cleaner.css', __FILE__ ) );
1009
+ wp_enqueue_script( 'media-cleaner', plugins_url( '/media-cleaner.js', __FILE__ ), array( 'jquery' ), "3.7.0", true );
1010
+ }
1011
+ }
1012
+
1013
+
1014
+ /*
1015
+ INSTALL / UNINSTALL
1016
+ */
1017
+
1018
+ register_activation_hook( __FILE__, 'wpmc_activate' );
1019
+ register_deactivation_hook( __FILE__, 'wpmc_uninstall' );
1020
+ register_uninstall_hook( __FILE__, 'wpmc_uninstall' );
1021
+
1022
+ function wpmc_reset () {
1023
+ wpmc_uninstall();
1024
+ wpmc_activate();
1025
+ }
1026
+
1027
+ function wpmc_activate () {
1028
+ global $wpdb;
1029
+ $table_name = $wpdb->prefix . "wpmcleaner";
1030
+ $charset_collate = $wpdb->get_charset_collate();
1031
+ $sql = "CREATE TABLE $table_name (
1032
+ id BIGINT(20) NOT NULL AUTO_INCREMENT,
1033
+ time DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL,
1034
+ type TINYINT(1) NOT NULL,
1035
+ postId BIGINT(20) NULL,
1036
+ path TINYTEXT NULL,
1037
+ size INT(9) NULL,
1038
+ ignored TINYINT(1) NOT NULL DEFAULT 0,
1039
+ deleted TINYINT(1) NOT NULL DEFAULT 0,
1040
+ issue TINYTEXT NOT NULL,
1041
+ UNIQUE KEY id (id)
1042
+ ) " . $charset_collate . ";";
1043
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
1044
+ dbDelta( $sql );
1045
+
1046
+ $upload_folder = wp_upload_dir();
1047
+ $basedir = $upload_folder['basedir'];
1048
+ if ( !is_writable( $basedir ) ) {
1049
+ echo '<div class="error"><p>' . __( 'The directory for uploads is not writable. Media Cleaner will only be able to scan.', 'media-cleaner' ) . '</p></div>';
1050
+ }
1051
+ }
1052
+
1053
+ function wpmc_uninstall () {
1054
+ global $wpdb;
1055
+ $table_name = $wpdb->prefix . "wpmcleaner";
1056
+ $wpdb->query("DROP TABLE IF EXISTS $table_name");
1057
+ }
trunk/media-cleaner.css ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .wp-core-ui .button-red {
2
+ background-color: #9B2124;
3
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#C5292E), to(#9B2124));
4
+ background-image: -webkit-linear-gradient(top, #C5292E, #9B2124);
5
+ background-image: -moz-linear-gradient(top, #C5292E, #9B2124);
6
+ background-image: -ms-linear-gradient(top, #C5292E, #9B2124);
7
+ background-image: -o-linear-gradient(top, #C5292E, #9B2124);
8
+ background-image: linear-gradient(to bottom, #C5292E, #9B2124);
9
+ border-color: #9B2124;
10
+ border-bottom-color: #8D1F21;
11
+ -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.5);
12
+ box-shadow: inset 0 1px 0 rgba(120,200,230,0.5);
13
+ color: #fff;
14
+ text-decoration: none;
15
+ text-shadow: 0 1px 0 rgba(0,0,0,0.1);
16
+ float: right;
17
+ }
18
+
19
+ .wp-core-ui .button-red.hover,
20
+ .wp-core-ui .button-red:hover,
21
+ .wp-core-ui .button-red.focus,
22
+ .wp-core-ui .button-red:focus {
23
+ background-color: #B72629;
24
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#D22E30), to(#9B2124));
25
+ background-image: -webkit-linear-gradient(top, #D22E30, #9B2124);
26
+ background-image: -moz-linear-gradient(top, #D22E30, #9B2124);
27
+ background-image: -ms-linear-gradient(top, #D22E30, #9B2124);
28
+ background-image: -o-linear-gradient(top, #D22E30, #9B2124);
29
+ background-image: linear-gradient(to bottom, #D22E30, #9B2124);
30
+ border-color: #7F1C1F;
31
+ -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.6);
32
+ box-shadow: inset 0 1px 0 rgba(120,200,230,0.6);
33
+ color: #fff;
34
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.3);
35
+ }
36
+
37
+ .wp-core-ui .button-red.focus,
38
+ .wp-core-ui .button-red:focus {
39
+ border-color: #500F0E;
40
+ -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.6), 1px 1px 2px rgba(0,0,0,0.4);
41
+ box-shadow: inset 0 1px 0 rgba(120,200,230,0.6), 1px 1px 2px rgba(0,0,0,0.4);
42
+ }
43
+
44
+ .wp-core-ui .button-red.active,
45
+ .wp-core-ui .button-red.active:hover,
46
+ .wp-core-ui .button-red.active:focus,
47
+ .wp-core-ui .button-red:active {
48
+ background: #7F1C1F;
49
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#9B2124), to(#B72629));
50
+ background-image: -webkit-linear-gradient(top, #9B2124, #B72629);
51
+ background-image: -moz-linear-gradient(top, #9B2124, #B72629);
52
+ background-image: -ms-linear-gradient(top, #9B2124, #B72629);
53
+ background-image: -o-linear-gradient(top, #9B2124, #B72629);
54
+ background-image: linear-gradient(to bottom, #9B2124, #B72629);
55
+ border-color: #601312 #AE2426 #AE2426 #AE2426;
56
+ color: rgba(255,255,255,0.95);
57
+ -webkit-box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
58
+ box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
59
+ text-shadow: 0 1px 0 rgba(0,0,0,0.1);
60
+ }
61
+
62
+ .wp-core-ui .button-red[disabled],
63
+ .wp-core-ui .button-red:disabled,
64
+ .wp-core-ui .button-red-disabled {
65
+ color: #E79496 !important;
66
+ background: #BA292B !important;
67
+ border-color: #7F1C1F !important;
68
+ -webkit-box-shadow: none !important;
69
+ box-shadow: none !important;
70
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.1) !important;
71
+ cursor: default;
72
+ }
trunk/media-cleaner.js ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ Plugin Name: Media Cleaner
3
+ Description: Clean your Media Library and Uploads Folder.
4
+ Author: Jordy Meow
5
+ */
6
+
7
+ function wpmc_pop_array(items, count) {
8
+ var newItems = [];
9
+ while ( newItems.length < count && items.length > 0 ) {
10
+ newItems.push( items.pop() );
11
+ }
12
+ return newItems;
13
+ }
14
+
15
+ /**
16
+ *
17
+ * RECOVER
18
+ *
19
+ */
20
+
21
+ function wpmc_recover() {
22
+ var items = [];
23
+ jQuery('#wpmc-table input:checked').each(function (index) {
24
+ if (jQuery(this)[0].value != 'on') {
25
+ items.push(jQuery(this)[0].value);
26
+ }
27
+ });
28
+ wpmc_recover_do(items, items.length);
29
+ }
30
+
31
+ function wpmc_recover_all() {
32
+ var items = [];
33
+ var data = { action: 'wpmc_get_all_deleted' };
34
+ jQuery.post(ajaxurl, data, function (response) {
35
+ reply = jQuery.parseJSON(response);
36
+ if ( !reply.success ) {
37
+ alert( reply.message );
38
+ return;
39
+ }
40
+ wpmc_recover_do(reply.results.ids, reply.results.ids.length);
41
+ });
42
+ }
43
+
44
+ function wpmc_recover_do(items, totalcount) {
45
+ wpmc_update_progress(totalcount - items.length, totalcount);
46
+ if (items.length > 0) {
47
+ newItems = wpmc_pop_array(items, 5);
48
+ data = { action: 'wpmc_recover_do', data: newItems };
49
+ }
50
+ else {
51
+ jQuery('#wpmc_progression').html("Done. Please <a href='?page=media-cleaner'>refresh</a> this page.");
52
+ return;
53
+ }
54
+ jQuery.post(ajaxurl, data, function (response) {
55
+ reply = jQuery.parseJSON(response);
56
+ if ( !reply.success ) {
57
+ alert( reply.message );
58
+ return;
59
+ }
60
+ wpmc_recover_do(items, totalcount);
61
+ });
62
+ }
63
+
64
+ /**
65
+ *
66
+ * DELETE
67
+ *
68
+ */
69
+
70
+ function wpmc_ignore() {
71
+ var items = [];
72
+ jQuery('#wpmc-table input:checked').each(function (index) {
73
+ if (jQuery(this)[0].value != 'on') {
74
+ items.push(jQuery(this)[0].value);
75
+ }
76
+ });
77
+ wpmc_ignore_do(items, items.length);
78
+ }
79
+
80
+ function wpmc_delete() {
81
+ var items = [];
82
+ jQuery('#wpmc-table input:checked').each(function (index) {
83
+ if (jQuery(this)[0].value != 'on') {
84
+ items.push(jQuery(this)[0].value);
85
+ }
86
+ });
87
+ wpmc_delete_do(items, items.length);
88
+ }
89
+
90
+ function wpmc_delete_all(isTrash) {
91
+ var items = [];
92
+ var data = { action: 'wpmc_get_all_issues', isTrash: isTrash ? 1 : 0 };
93
+ jQuery.post(ajaxurl, data, function (response) {
94
+ reply = jQuery.parseJSON(response);
95
+ if ( !reply.success ) {
96
+ alert( reply.message );
97
+ return;
98
+ }
99
+ wpmc_delete_do(reply.results.ids, reply.results.ids.length);
100
+ });
101
+ }
102
+
103
+ function wpmc_update_progress(current, totalcount) {
104
+ jQuery('#wpmc_progression').html('<span class="dashicons dashicons-controls-play"></span> Analyzing ' + current + "/" + totalcount + " (" + Math.round(current / totalcount * 100) + "%)");
105
+ }
106
+
107
+ function wpmc_delete_do(items, totalcount) {
108
+ wpmc_update_progress(totalcount - items.length, totalcount);
109
+ if (items.length > 0) {
110
+ newItems = wpmc_pop_array(items, 5);
111
+ data = { action: 'wpmc_delete_do', data: newItems };
112
+ }
113
+ else {
114
+ jQuery('#wpmc_progression').html("Done. Please <a href='?page=media-cleaner'>refresh</a> this page.");
115
+ return;
116
+ }
117
+ jQuery.post(ajaxurl, data, function (response) {
118
+ reply = jQuery.parseJSON(response);
119
+ if ( !reply.success ) {
120
+ alert( reply.message );
121
+ return;
122
+ }
123
+ wpmc_delete_do(items, totalcount);
124
+ });
125
+ }
126
+
127
+ function wpmc_ignore_do(items, totalcount) {
128
+ wpmc_update_progress(totalcount - items.length, totalcount);
129
+ if (items.length > 0) {
130
+ newItems = wpmc_pop_array(items, 5);
131
+ data = { action: 'wpmc_ignore_do', data: newItems };
132
+ }
133
+ else {
134
+ jQuery('#wpmc_progression').html("Done. Please <a href='?page=media-cleaner'>refresh</a> this page.");
135
+ return;
136
+ }
137
+ jQuery.post(ajaxurl, data, function (response) {
138
+ reply = jQuery.parseJSON(response);
139
+ if ( !reply.success ) {
140
+ alert( reply.message );
141
+ return;
142
+ }
143
+ wpmc_ignore_do(items, totalcount);
144
+ });
145
+ }
146
+
147
+ /**
148
+ *
149
+ * SCAN
150
+ *
151
+ */
152
+
153
+ var wpmc = {
154
+ dirs: [],
155
+ files: [],
156
+ medias: [],
157
+ total: 0,
158
+ issues: 0
159
+ };
160
+
161
+ // WPMC GET INITIAL INFO
162
+
163
+ function wpmc_scan_type_finished() {
164
+
165
+ }
166
+
167
+ function wpmc_scan_type_next(type, path) {
168
+
169
+ }
170
+
171
+ function wpmc_scan_type(type, path = null, limit = 0) {
172
+ var data = { action: 'wpmc_scan', medias: type === 'medias', files: type === 'files', path: path, limit: limit };
173
+ if (path) {
174
+ elpath = path.replace(/^.*[\\\/]/, '');
175
+ jQuery('#wpmc_progression').html('<span class="dashicons dashicons-portfolio"></span> Preparing files (' + elpath + ')...');
176
+ }
177
+ else if (type === 'medias')
178
+ jQuery('#wpmc_progression').html('<span class="dashicons dashicons-admin-media"></span> Preparing medias (' + limit + ' media)....');
179
+ else
180
+ jQuery('#wpmc_progression').html('<span class="dashicons dashicons-portfolio"></span> Preparing files...');
181
+
182
+
183
+ jQuery.post(ajaxurl, data, function (response) {
184
+ reply = jQuery.parseJSON(response);
185
+ if ( !reply.success ) {
186
+ alert( reply.message );
187
+ return;
188
+ }
189
+
190
+ // Store results
191
+ for (var i = 0, len = reply.results.length; i < len; i++) {
192
+ var r = reply.results[i];
193
+ if (type === 'files') {
194
+ if ( r.type === 'dir' )
195
+ wpmc.dirs.push( r.path );
196
+ else if ( r.type === 'file' ) {
197
+ wpmc.files.push( r.path );
198
+ wpmc.total++;
199
+ }
200
+ }
201
+ else if (type === 'medias') {
202
+ wpmc.medias.push( r );
203
+ wpmc.total++;
204
+ }
205
+ }
206
+
207
+ // Next query
208
+ if (type === 'medias') {
209
+ if (wpmc_cfg.scanFiles || !reply.finished)
210
+ return wpmc_scan_type('medias', null, reply.limit);
211
+ else
212
+ return wpmc_scan_do();
213
+ }
214
+ else if (type === 'files') {
215
+ var dir = wpmc.dirs.pop();
216
+ if (dir)
217
+ return wpmc_scan_type('files', dir);
218
+ else
219
+ return wpmc_scan_do();
220
+ }
221
+ });
222
+ }
223
+
224
+ function wpmc_scan() {
225
+ wpmc = { dirs: [], files: [], medias: [], total: 0, issues: 0 };
226
+ if (wpmc_cfg.scanMedia)
227
+ wpmc_scan_type('medias', null);
228
+ else if (wpmc_cfg.scanFiles)
229
+ wpmc_scan_type('files', null);
230
+ }
231
+
232
+ function wpmc_scan_do() {
233
+ wpmc_update_progress(wpmc.total - (wpmc.files.length + wpmc.medias.length), wpmc.total);
234
+ var data = {};
235
+ var expectedSuccess = 0;
236
+ if (wpmc.files.length > 0) {
237
+ newFiles = wpmc_pop_array(wpmc.files, 5);
238
+ expectedSuccess = newFiles.length;
239
+ data = { action: 'wpmc_scan_do', type: 'file', data: newFiles };
240
+ }
241
+ else if (wpmc.medias.length > 0) {
242
+ newMedias = wpmc_pop_array(wpmc.medias, 5);
243
+ expectedSuccess = newMedias.length;
244
+ data = { action: 'wpmc_scan_do', type: 'media', data: newMedias };
245
+ }
246
+ else {
247
+ jQuery('#wpmc_progression').html(wpmc.issues + " issue(s) found. <a href='?page=media-cleaner'></span>Refresh</a>.");
248
+ return;
249
+ }
250
+ jQuery.post(ajaxurl, data, function (response) {
251
+ try {
252
+ reply = jQuery.parseJSON(response);
253
+ }
254
+ catch (e) {
255
+ reply = null;
256
+ }
257
+ if (!reply) {
258
+ reply = { success: false, message: "The reply from the server is broken. The reply will be displayed in your Javascript console. You should also check your PHP Error Logs." };
259
+ console.debug( "Media File Cleaner got this reply from the server: " + response);
260
+ }
261
+ if ( !reply.success ) {
262
+ alert( reply.message );
263
+ }
264
+ if (reply.result) {
265
+ wpmc.issues += expectedSuccess - reply.result.success;
266
+ }
267
+ wpmc_scan_do();
268
+ });
269
+ }
270
+
271
+ /**
272
+ *
273
+ * INIT
274
+ *
275
+ */
276
+
277
+ jQuery('#wpmc-cb-select-all').on('change', function (cb) {
278
+ jQuery('#wpmc-table input').prop('checked', cb.target.checked);
279
+ });
trunk/media-cleaner.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Media Cleaner
4
+ Plugin URI: http://meowapps.com
5
+ Description: Clean your Media Library, many options, trash system.
6
+ Version: 4.0.2
7
+ Author: Jordy Meow
8
+ Author URI: http://meowapps.com
9
+ Text Domain: media-cleaner
10
+
11
+ Big thanks to Matt (http://www.twistedtek.net/) for all his
12
+ contributions made to the plugin.
13
+
14
+ Originally developed for two of my websites:
15
+ - Jordy Meow (http://jordymeow.com)
16
+ - Haikyo (http://www.haikyo.org)
17
+ */
18
+
19
+ if ( is_admin() ) {
20
+
21
+ global $wpmc_version;
22
+ $wpmc_version = '4.0.2';
23
+
24
+ // Admin
25
+ require( 'wpmc_admin.php' );
26
+ $wpmc_admin = new Meow_WPMC_Admin( 'wpmc', __FILE__, 'media-cleaner' );
27
+
28
+ // Core
29
+ require( 'core.php' );
30
+ new Meow_WPMC_Core( $wpmc_admin );
31
+
32
+ /*******************************************************************************
33
+ * TODO: OLD PRO, THIS FUNCTION SHOULD BE REMOVED IN THE FUTURE
34
+ ******************************************************************************/
35
+
36
+ add_action( 'admin_notices', 'wpmc_meow_old_version_admin_notices' );
37
+
38
+ function wpmc_meow_old_version_admin_notices() {
39
+ if ( isset( $_POST['wpmc_reset_sub'] ) ) {
40
+ delete_transient( 'wpmc_validated' );
41
+ delete_option( 'wpmc_pro_serial' );
42
+ delete_option( 'wpmc_pro_status' );
43
+ }
44
+ $subscr_id = get_option( 'wpmc_pro_serial', "" );
45
+ if ( empty( $subscr_id ) )
46
+ return;
47
+ $forever = strpos( $subscr_id, 'F-' ) !== false;
48
+ $yearly = strpos( $subscr_id, 'I-' ) !== false;
49
+ if ( !$forever && !$yearly )
50
+ return;
51
+ ?>
52
+ <div class="error">
53
+ <p>
54
+ <h2>IMPORTANT MESSAGE ABOUT MEDIA CLEANER</h2>
55
+ 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>.
56
+ </p>
57
+ <p>
58
+ <form method="post" action="">
59
+ <input type="hidden" name="wpmc_reset_sub" value="true">
60
+ <input type="submit" name="submit" id="submit" class="button" value="Got it. Clear this!">
61
+ <br /><small><b>Make sure you followed the instruction before clicking this button.</b></small>
62
+ </form>
63
+ </p>
64
+ </div>
65
+ <?php
66
+ }
67
+
68
+ }
69
+
70
+ ?>
trunk/readme.txt ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Media Cleaner ===
2
+ Contributors: TigrouMeow
3
+ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H2S7S3G4XMJ6J
4
+ Tags: management, admin, file, files, images, image, media, library, upload, clean, cleaning
5
+ Requires at least: 4.2
6
+ Tested up to: 4.7
7
+ Stable tag: 4.0.2
8
+
9
+ Clean your Media Library and Uploads directory. It has an internal trash and recovery features.
10
+
11
+ == Description ==
12
+
13
+ Clean your Media Library from the media which aren't used in any of your posts, gallery and so on. It features an internal trash, moving the files in there temporarily for you to make sure the files aren't actually in used; once checked, you can trash them permanently. **Before using this plugin, make sure you have a proper backup of your files and database. This is the most important step on the usage of this plugin as you can't trust any file deletion tools.** The Pro version of this plugin brings also scanning to the /uploads folder and will detect which files aren't registered in the Media Library, not used in your content and so on. Retina images are also detected and supported, shortcodes, HTML in sidebars and of course your posts, pages and all post types.
14
+
15
+ **This tool is a knife. I try to make this knife perfect. However, this is still a knife, and in the hands of somebody who doesn't understand it and just use it without thinking, it might end badly. Don't use it you you don't have any backup, or if you don't know what it does.**
16
+
17
+ **INCOMPATIBILITY**. If you are not using WordPress naturally and using plugin to edit your posts, this plugin will not be able to detect how your images are used. For instance, Visual Composer is not supported as for now.
18
+
19
+ **UNIQUE PLUGIN**. Such a plugin is difficult to create and to maintain. If you understand WordPress, you probably know why. This plugin tries its best to help you. Get used to it and you will get awesome results. This is the only plugin to propose those functions and even a dashboard to cleanup your WordPress install from unused files.
20
+
21
+ **DASHBOARD**. Those file will be shown in a specific dashboard. At this point, it will be up to you to delete them. Files detected as un-used are added to a specific dashboard where you can choose to trash them. They will be then moved to a trash internal to the plugin. After more testing, you can trash them definitely.
22
+
23
+ **FREE / PRO**. The Free version of the plugin works with the media available in your Media Library. The Pro version adds file scanning to your physical /uploads directory.
24
+
25
+ **AGAIN, BE CAREFUL**. Again, this plugin deletes files so... be careful! Backup is not only important, it is **necessary**. Don't use this plugin if you don't understand how WordPress works. This is a knife, you need to understand what it does and how before using it.
26
+
27
+ It has been tested with WP Retina 2x and WPML.
28
+
29
+ == Installation ==
30
+
31
+ 1. Upload `media-file-cleaner` to the `/wp-content/plugins/` directory
32
+ 2. Activate the plugin through the 'Plugins' menu in WordPress
33
+ 3. Go in the Settings -> Media Cleaner and check the appropriate options
34
+ 3. Go in Media -> Media Cleaner
35
+
36
+ == Upgrade Notice ==
37
+
38
+ Replace all the files. Nothing else to do.
39
+
40
+ == Frequently Asked Questions ==
41
+
42
+ = Is it safe? =
43
+ No! :) How can a plugin that deletes files be 100% safe? ;) I did my best (and will improve it in every way I can) but it is impossible to cover all the cases. On a normal WordPress install it should work perfectly, however other themes and plugins can do whatever they want do and register files in their own way, not always going through the API. I ran it on a few big websites and it performed very well. Make a backup (database + uploads directory) then run it. Again, I insist: BACKUP, BACKUP, BACKUP! Don't come here to complain that it deleted your files, because, yes, it deletes files. The plugin tries its best to help you and it is the only plugin that does it well.
44
+
45
+ = What is 'Reset' doing exactly? =
46
+ It re-creates the Media Cleaner table in the database. You will need to re-run the scan after this.
47
+
48
+ == Screenshots ==
49
+
50
+ 1. Media -> Media Cleaner
51
+
52
+ == Changelog ==
53
+
54
+ = 4.0.2 =
55
+ * Add: Information about how a certain media is used (Edit Media screen).
56
+ * Fix: Check / Create DB process.
57
+ * Fix: Plugin was not working well with themes using Background/Header.
58
+ * Update: A bit of cleaning.
59
+
60
+ = 4.0.0 =
61
+ * Update: Core was re-organized and cleaned. Ready for nice updates.
62
+
63
+ = 3.7.0 =
64
+ * Fix: Little issue when inserting the serial key for the first time.
65
+ * Update: Compliance with the WordPress.org rules, new licensing system.
66
+ * Update: Moved assets.
67
+ * Info: There will be an important warning showing up during this update. It is an important annoucement.
68
+
69
+ = 3.6.4 =
70
+ * Fix: Plugin was not working properly with broken Media metadata. It now handles it properly.
71
+ * 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.
72
+
73
+ = 3.6.2 =
74
+ * Fix: When over 1 GO, was displaying a lower size value.
75
+ * Fix: Counting wasn't exact with a Filesystem scan.
76
+ * Info: Please read the previous changelog as it didn't appear in WP for some reason.
77
+ * Add: Check Posts also look for the Media ID in the classes (more secure).
78
+ * 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.
79
+
80
+ = 3.6.0 =
81
+ * 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.
82
+ * Update: Nicer internal icons rather than the old images for the UI.
83
+ * Update: Faster and safer for post_content checks.
84
+ * Update: This is a big one. The plugin is more clear about what it does. You need to choose either to scan the Media or the Filesystem, and also against what exactly. There has also been a few fixes and it will work on more big installs. If it fails, you can remove a few scanning options, and I will continue to work on making it perfect to support huge installs with all the options on.
85
+
86
+ = 3.2.8 =
87
+ * Update: Show a better edit media screen.
88
+ * Update: Will show the same number of items as in the Media Library (before it was fixed to 15 items per page).
89
+ * Fix: Was displaying warning if the number of items per page in the Media page is not set.
90
+
91
+ = 3.2.0 =
92
+ * Fix: HTML adapted to WP 4.5.1.
93
+ * Fix: Doesn't break if there is an error on the server-side. Display an alert and continue.
94
+ * Update: Can select more than one file for non-Pro.
95
+ * Fix: Issue with PHP 7.
96
+
97
+ = 3.0.0 =
98
+ * Add: Option for resolving shortcode during analysis.
99
+ * Update: French translation. Big thanks to Guillaume (and also for all his testing!).
100
+ * Info: New name, fresh start. This plugin changed completely since it very first release :)
101
+
102
+ = 2.5.0 =
103
+ * Add: Delete the unused directories.
104
+ * Add: Doesn't break when there are too many files in the system.
105
+ * Add: Pro version with better support.
106
+ * Update: Improved detection of unused files.
107
+ * Fix: UTF8 filenames skipped by default but can be scanned through an option.
108
+ * Fix: Really many fixes :)
109
+ * Info: Contact me if you have been using the plugin for a long time and love it.
110
+
111
+ = 2.4.2 =
112
+ * Add: Inclusion of gallery post format images.
113
+ * Fix: Better gallery URL matching.
114
+ * Info: Thanks to syntax53 for those improvements via GitHub (https://github.com/tigroumeow/media-file-cleaner/pull/3). Please review Media Cleaner if you like it. The plugin needs reviews to live. Thank you :) (https://wordpress.org/support/view/plugin-reviews/media-file-cleaner)
115
+
116
+ = 2.4.0 =
117
+ * Fix: Cross site scripting vulnerability fixes.
118
+ * Change: Many enhancements and fixes made by Matt (http://www.twistedtek.net/). Please thanks him :)
119
+ * Info: Please perform a "Reset" in the plugin dashboard after installing this new version.
120
+
121
+ = 2.2.6 =
122
+ * Fix: Scan for multisite.
123
+ * Change: options are now all enabled by default.
124
+ * Fix: DB issue avoided trashed files from being deleted permanently.
125
+
126
+ = 2.0.2 =
127
+ * Works with WP 4.
128
+ * Gallery support.
129
+ * Fix: IGNORE function was... ignored by the scanning process.
130
+
131
+ = 1.9.0 =
132
+ * Add: thumbnails.
133
+ * Add: IGNORE function.
134
+ * Change: cosmetic changes.
135
+ * Add: now detects the custom header and custom background.
136
+ * Change: the CSS was updated to fit the new Admin theme.
137
+
138
+ = 1.7.0 =
139
+ * Change: the MEDIA files are now going to the trash but the MEDIA reference in the DB is still removed permanently.
140
+ * Stable release.
141
+ * Change: Readme.txt.
142
+
143
+ = 1.4.0 =
144
+ * Add: check the meta properties.
145
+ * Add: check the 'featured image' properties.
146
+ * Fix: keep the trash information when a new scan is started.
147
+ * Fix: remove the DB on uninstall, not on desactivate.
148
+
149
+ = 1.2.2 =
150
+ * Add: progress %.
151
+ * Fix: issues with apostrophes in filenames.
152
+ * Change: UI cleaning.
153
+
154
+ = 1.2.0 =
155
+ * Add: options (scan files / scan media).
156
+ * Fix: mkdir issues.
157
+ * Change: operations are buffered by 5 (faster).
158
+
159
+ = 0.1.0 =
160
+ * First release.
trunk/wpmc_admin.php ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ include "common/admin.php";
4
+
5
+ class Meow_WPMC_Admin extends MeowApps_Admin {
6
+
7
+ public function __construct( $prefix, $mainfile, $domain ) {
8
+ parent::__construct( $prefix, $mainfile, $domain );
9
+ add_action( 'admin_menu', array( $this, 'app_menu' ) );
10
+ add_action( 'admin_notices', array( $this, 'admin_notices' ) );
11
+ $method = get_option( 'wpmc_method', 666 );
12
+ if ( $method == 666 )
13
+ $this->initial_setup();
14
+ }
15
+
16
+ function admin_notices() {
17
+ if ( !$this->is_registered() && get_option( 'wpmc_method', 'media' ) == 'files' ) {
18
+ _e( "<div class='error'><p>The Pro version is required to scan files. You can <a target='_blank' href='http://meowapps.com/media-cleaner'>get a serial for the Pro version here</a>.</p></div>", 'media-cleaner' );
19
+ }
20
+ }
21
+
22
+ function initial_setup() {
23
+ update_option( 'wpmc_method', 'media', false );
24
+ update_option( 'wpmc_posts', false, false );
25
+ update_option( 'wpmc_galleries', false, false );
26
+ update_option( 'wpmc_postmeta', false, false );
27
+ $shortcode = $this->old_getoption( 'shortcode', 'wpmc_basics', false );
28
+ update_option( 'wpmc_shortcode', $shortcode, false );
29
+ $utf8_support = $this->old_getoption( 'scan_non_ascii', 'wpmc_basics', false );
30
+ update_option( 'wpmc_utf8', $utf8_support, false );
31
+ $hide_thumbnails = $this->old_getoption( 'hide_thumbnails', 'wpmc_basics', false );
32
+ update_option( 'wpmc_hide_thumbnails', $hide_thumbnails, false );
33
+ $hide_warning = $this->old_getoption( 'hide_warning', 'wpmc_basics', false );
34
+ update_option( 'wpmc_hide_warning', $hide_warning, false );
35
+ delete_option( 'wpmc_basics' );
36
+ delete_option( 'wpmc_pro' );
37
+ }
38
+
39
+ function common_url( $file ) {
40
+ return trailingslashit( plugin_dir_url( __FILE__ ) ) . 'common/' . $file;
41
+ }
42
+
43
+ function app_menu() {
44
+
45
+ // SUBMENU > Settings
46
+ add_submenu_page( 'meowapps-main-menu', 'Media Cleaner', 'Media Cleaner', 'manage_options',
47
+ 'wpmc_settings-menu', array( $this, 'admin_settings' ) );
48
+
49
+ // SUBMENU > Settings > Settings
50
+ add_settings_section( 'wpmc_settings', null, null, 'wpmc_settings-menu' );
51
+ add_settings_field( 'wpmc_method', "Method",
52
+ array( $this, 'admin_method_callback' ),
53
+ 'wpmc_settings-menu', 'wpmc_settings' );
54
+ if ( get_option( 'wpmc_method', 'media' ) == 'files' ) {
55
+ add_settings_field( 'wpmc_media_library', "Media Library",
56
+ array( $this, 'admin_media_library_callback' ),
57
+ 'wpmc_settings-menu', 'wpmc_settings' );
58
+ }
59
+ add_settings_field( 'wpmc_posts', "Posts",
60
+ array( $this, 'admin_posts_callback' ),
61
+ 'wpmc_settings-menu', 'wpmc_settings' );
62
+ add_settings_field( 'wpmc_postmeta', "Post Meta",
63
+ array( $this, 'admin_postmeta_callback' ),
64
+ 'wpmc_settings-menu', 'wpmc_settings' );
65
+ add_settings_field( 'wpmc_galleries', "Galleries",
66
+ array( $this, 'admin_galleries_callback' ),
67
+ 'wpmc_settings-menu', 'wpmc_settings' );
68
+ add_settings_field( 'wpmc_widgets', "Widgets",
69
+ array( $this, 'admin_widgets_callback' ),
70
+ 'wpmc_settings-menu', 'wpmc_settings' );
71
+
72
+ if ( get_option( 'wpmc_posts', false ) ) {
73
+ add_settings_field( 'wpmc_shortcode', "Shortcodes<br />(Pro)",
74
+ array( $this, 'admin_shortcode_callback' ),
75
+ 'wpmc_settings-menu', 'wpmc_settings' );
76
+ }
77
+ add_settings_field( 'wpmc_utf8', "UTF-8",
78
+ array( $this, 'admin_utf8_callback' ),
79
+ 'wpmc_settings-menu', 'wpmc_settings' );
80
+
81
+ // SUBMENU > Settings > Settings
82
+ add_settings_section( 'wpmc_ui_settings', null, null, 'wpmc_ui_settings-menu' );
83
+ add_settings_field( 'wpmc_hide_thumbnails', "Thumbnails",
84
+ array( $this, 'admin_hide_thumbnails_callback' ),
85
+ 'wpmc_ui_settings-menu', 'wpmc_ui_settings' );
86
+ add_settings_field( 'wpmc_hide_warning', "Warning Message (Pro)",
87
+ array( $this, 'admin_hide_warning_callback' ),
88
+ 'wpmc_ui_settings-menu', 'wpmc_ui_settings' );
89
+
90
+ // SETTINGS
91
+ register_setting( 'wpmc_settings', 'wpmc_method' );
92
+ register_setting( 'wpmc_settings', 'wpmc_posts' );
93
+ register_setting( 'wpmc_settings', 'wpmc_shortcode' );
94
+ register_setting( 'wpmc_settings', 'wpmc_galleries' );
95
+ register_setting( 'wpmc_settings', 'wpmc_widgets' );
96
+ register_setting( 'wpmc_settings', 'wpmc_media_library' );
97
+ register_setting( 'wpmc_settings', 'wpmc_postmeta' );
98
+ register_setting( 'wpmc_settings', 'wpmc_utf8' );
99
+
100
+ register_setting( 'wpmc_ui_settings', 'wpmc_hide_thumbnails' );
101
+ register_setting( 'wpmc_ui_settings', 'wpmc_hide_warning' );
102
+ }
103
+
104
+ function admin_settings() {
105
+ ?>
106
+ <div class="wrap">
107
+ <?php
108
+ echo $this->display_title( "Media Cleaner" );
109
+ ?>
110
+ <p>This plugin will help you cleaning your WordPress install.</p>
111
+ <div class="meow-section meow-group">
112
+ <div class="meow-box meow-col meow-span_2_of_2">
113
+ <h3>How to use</h3>
114
+ <div class="inside">
115
+ <?php echo _e( "You can choose thow kind of methods, analyzing your Media Library for images which are not in used, or in your Filesystem for images which aren't registered in the Media Library or not in used. <b>Those checks can be very expensive in term of resources and might fail so you might want to play with those options depending on your install and what you need. I am working actively on making the plugin to work fine even on huge installs with all those options.</b>", 'media-cleaner' ); ?>
116
+ </div>
117
+
118
+ </div>
119
+ </div>
120
+
121
+ <div class="meow-section meow-group">
122
+
123
+ <div class="meow-col meow-span_1_of_2">
124
+ <div class="meow-box">
125
+ <h3>Scanning</h3>
126
+ <div class="inside">
127
+ <form method="post" action="options.php">
128
+ <?php settings_fields( 'wpmc_settings' ); ?>
129
+ <?php do_settings_sections( 'wpmc_settings-menu' ); ?>
130
+ <?php submit_button(); ?>
131
+ </form>
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ <div class="meow-col meow-span_1_of_2">
137
+ <?php $this->display_serialkey_box( "https://meowapps.com/media-cleaner/" ); ?>
138
+
139
+ <div class="meow-box">
140
+ <h3>Scanning</h3>
141
+ <div class="inside">
142
+ <form method="post" action="options.php">
143
+ <?php settings_fields( 'wpmc_ui_settings' ); ?>
144
+ <?php do_settings_sections( 'wpmc_ui_settings-menu' ); ?>
145
+ <?php submit_button(); ?>
146
+ </form>
147
+ </div>
148
+ </div>
149
+
150
+ <?php if ( get_option( 'wpmc_shortcode', false ) ): ?>
151
+ <div class="meow-box">
152
+ <h3>Shortcodes</h3>
153
+ <div class="inside">
154
+ <p>Here are the shortcodes registered in your WordPress by your theme and other plugins.</p>
155
+ <?php
156
+ global $shortcode_tags;
157
+ try {
158
+ $allshortcodes = array_diff( $shortcode_tags, array( ) );
159
+ $my_shortcodes = array();
160
+ foreach ( $allshortcodes as $sc )
161
+ if ( $sc != '__return_false' ) {
162
+ if ( is_string( $sc ) )
163
+ array_push( $my_shortcodes, str_replace( '_shortcode', '', (string)$sc ) );
164
+ }
165
+ $my_shortcodes = implode( '<br />', $my_shortcodes );
166
+ }
167
+ catch (Exception $e) {
168
+ $my_shortcodes = "";
169
+ }
170
+ echo $my_shortcodes;
171
+ ?>
172
+ </div>
173
+ </div>
174
+ <?php endif; ?>
175
+
176
+ </div>
177
+
178
+ </div>
179
+ </div>
180
+ <?php
181
+ }
182
+
183
+
184
+
185
+ /*
186
+ OPTIONS CALLBACKS
187
+ */
188
+
189
+ function admin_method_callback( $args ) {
190
+ $value = get_option( 'wpmc_method', 'media' );
191
+ $html = '<select id="wpmc_method" name="wpmc_method">
192
+ <option ' . selected( 'media', $value, false ) . 'value="media">Media Library</option>
193
+ <option ' . disabled( $this->is_registered(), false, false ) . ' ' . selected( 'files', $value, false ) . 'value="files">Filesystem (Pro)</option>
194
+ </select><small><br /><br />' . __( '<b>Media Library</b>: The medias from Media Library which seem not being used in your WordPress will be marked as to be deleted. <br /><br /><b>Filesystem</b>: The files in your /uploads directory that don\'t seem being used in your WordPress will be marked as to be deleted. If the files are registered as a media in your Media Library, they will be considered as fine (even if they are not used in the content of your website).', 'media-cleaner' ) . '</small>';
195
+ echo $html;
196
+ }
197
+
198
+
199
+ function admin_shortcode_callback( $args ) {
200
+ $value = get_option( 'wpmc_shortcode', null );
201
+ $html = '<input ' . disabled( $this->is_registered(), false, false ) . ' type="checkbox" id="wpmc_shortcode" name="wpmc_shortcode" value="1" ' .
202
+ checked( 1, get_option( 'wpmc_shortcode' ), false ) . '/>';
203
+ $html .= '<label>Analyze</label><br /><small>The shortcodes you are using in your <b>posts</b> and/or <b>widgets</b> (depending on your options) will be resolved and analyzed. This process takes resources and if the scanning suddenly stops, this might be the cause. You don\'t need to have this option enabled for the WP Gallery (this is covered by the Galleries option).</small>';
204
+ echo $html;
205
+ }
206
+
207
+ function admin_utf8_callback( $args ) {
208
+ $value = get_option( 'wpmc_utf8', null );
209
+ $html = '<input type="checkbox" id="wpmc_utf8" name="wpmc_utf8" value="1" ' .
210
+ checked( 1, get_option( 'wpmc_utf8' ), false ) . '/>';
211
+ $html .= __( '<label>Do not skip UTF-8 filenames</label><br /><small>PHP does not always work well with UTF-8 on all systems. If the scanning suddenly stops, this might be the cause.</small>', 'media-cleaner' );
212
+ echo $html;
213
+ }
214
+
215
+ function admin_media_library_callback( $args ) {
216
+ $value = get_option( 'wpmc_media_library', null );
217
+ $html = '<input type="checkbox" id="wpmc_media_library" name="wpmc_media_library" value="1" ' .
218
+ checked( 1, get_option( 'wpmc_media_library' ), false ) . '/>';
219
+ $html .= '<label>Check</label><br /><small>Checks if the file is part of a Media (as a full-size or alternative size). This option is of course only available for the Filesystem method.</small>';
220
+ echo $html;
221
+ }
222
+
223
+ function admin_galleries_callback( $args ) {
224
+ $value = get_option( 'wpmc_galleries', null );
225
+ $html = '<input type="checkbox" id="wpmc_galleries" name="wpmc_galleries" value="1" ' .
226
+ checked( 1, get_option( 'wpmc_galleries' ), false ) . '/>';
227
+ $html .= '<label>Analyze</label><br /><small>Checks if the file is used in a WP Gallery (which are in posts or any post-type).</small>';
228
+ echo $html;
229
+ }
230
+
231
+ function admin_posts_callback( $args ) {
232
+ $value = get_option( 'wpmc_posts', false );
233
+ $html = '<input type="checkbox" id="wpmc_posts" name="wpmc_posts" value="1" ' .
234
+ checked( 1, get_option( 'wpmc_posts' ), false ) . '/>';
235
+ $html .= '<label>Analyze</label><br /><small>Check if the file is used in posts (that includes any post-type: pages, products and others) and, in addition, in case of the Media, check if it used in the IMG\'s class.</small>';
236
+ echo $html;
237
+ }
238
+
239
+ function admin_postmeta_callback( $args ) {
240
+ $value = get_option( 'wpmc_postmeta', false );
241
+ $html = '<input type="checkbox" id="wpmc_postmeta" name="wpmc_postmeta" value="1" ' .
242
+ checked( 1, get_option( 'wpmc_postmeta' ), false ) . '/>';
243
+ $html .= '<label>Analyze</label><br /><small>Checks if the file is used in the Post Meta.</small>';
244
+ echo $html;
245
+ }
246
+
247
+ function admin_widgets_callback( $args ) {
248
+ $value = get_option( 'wpmc_widgets', false );
249
+ $html = '<input type="checkbox" id="wpmc_widgets" name="wpmc_widgets" value="1" ' .
250
+ checked( 1, get_option( 'wpmc_widgets' ), false ) . '/>';
251
+ $html .= '<label>Analyze</label><br /><small>Checks if the file is used in widgets.</small>';
252
+ echo $html;
253
+ }
254
+
255
+ function admin_hide_thumbnails_callback( $args ) {
256
+ $value = get_option( 'wpmc_hide_thumbnails', null );
257
+ $html = '<input type="checkbox" id="wpmc_hide_thumbnails" name="wpmc_hide_thumbnails" value="1" ' .
258
+ checked( 1, get_option( 'wpmc_hide_thumbnails' ), false ) . '/>';
259
+ $html .= '<label>Hide</label><br /><small>If you prefer not to see the thumbnails.</small>';
260
+ echo $html;
261
+ }
262
+
263
+ function admin_hide_warning_callback( $args ) {
264
+ $value = get_option( 'wpmc_hide_warning', null );
265
+ $html = '<input ' . disabled( $this->is_registered(), false, false ) . ' type="checkbox" id="wpmc_hide_warning" name="wpmc_hide_warning" value="1" ' .
266
+ checked( 1, get_option( 'wpmc_hide_warning' ), false ) . '/>';
267
+ $html .= '<label>Hide</label><br /><small>Have you read it twice? If yes, hide it :)</small>';
268
+ echo $html;
269
+ }
270
+
271
+ /**
272
+ *
273
+ * GET / SET OPTIONS (TO REMOVE)
274
+ *
275
+ */
276
+
277
+ function old_getoption( $option, $section, $default = '' ) {
278
+ $options = get_option( $section );
279
+ if ( isset( $options[$option] ) ) {
280
+ if ( $options[$option] == "off" ) {
281
+ return false;
282
+ }
283
+ if ( $options[$option] == "on" ) {
284
+ return true;
285
+ }
286
+ return $options[$option];
287
+ }
288
+ return $default;
289
+ }
290
+
291
+ }
292
+
293
+ ?>
trunk/wpmc_checkers.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // This file will contains all the CORE checkers of the Media Cleaner system.
4
+ // Each 'checker' checks the usage of the file by a certain feature of WordPress.
5
+
6
+ class Meow_WPMC_Checkers {
7
+
8
+ private $core;
9
+
10
+ public function __construct( $core ) {
11
+ $this->core = $core;
12
+ }
13
+
14
+ function has_background_or_header( $file ) {
15
+ if ( current_theme_supports( 'custom-header' ) ) {
16
+ $custom_header = get_custom_header();
17
+ if ( $custom_header && $custom_header->url ) {
18
+ if ( strpos( $custom_header->url, $file ) !== false ) {
19
+ if ($this->core->debug)
20
+ error_log("{$file} found in header");
21
+ return true;
22
+ }
23
+ }
24
+ }
25
+
26
+ if ( current_theme_supports( 'custom-background' ) ) {
27
+ $custom_background = get_theme_mod('background_image');
28
+ if ( $custom_background ) {
29
+ if ( strpos( $custom_background, $file ) !== false ) {
30
+ if ($this->core->debug)
31
+ error_log("{$file} found in background");
32
+ return true;
33
+ }
34
+ }
35
+ }
36
+
37
+ return false;
38
+ }
39
+
40
+ function wpmc_check_in_gallery( $file ) {
41
+
42
+ if ( !get_option( 'wpmc_galleries', false ) )
43
+ return false;
44
+
45
+ $file = $this->core->wpmc_clean_uploaded_filename( $file );
46
+ $uploads = wp_upload_dir();
47
+ $parsedURL = parse_url( $uploads['baseurl'] );
48
+ $regex_match_file = '(' . preg_quote( $file ) . ')';
49
+ $regex = addcslashes( '(?:(?:http(?:s)?\\:)?//' .
50
+ preg_quote( $parsedURL['host'] ).')?' .
51
+ preg_quote( $parsedURL['path'] ) . '/' . $regex_match_file, '/');
52
+ $images = $this->core->wpmc_get_galleries_images();
53
+ foreach ( $images as $image ) {
54
+ $found = preg_match('/'.$regex.'/i', $image);
55
+ if ( $this->core->debug && $found )
56
+ error_log("{$file} found in a galllery");
57
+ if ( $found )
58
+ return true;
59
+ }
60
+ return false;
61
+ }
62
+
63
+ function has_meta( $file, $attachment_id = 0 ) {
64
+
65
+ if ( !get_option( 'wpmc_postmeta', false ) )
66
+ return false;
67
+
68
+ global $wpdb;
69
+ $uploads = wp_upload_dir();
70
+ $parsedURL = parse_url( $uploads['baseurl'] );
71
+ $file = $this->core->wpmc_clean_uploaded_filename( $file );
72
+ $regex_match_file = '(' . preg_quote( $file ) . ')';
73
+ $regex = addcslashes( '(?:(?:(?:http(?:s)?\\:)?//' .
74
+ preg_quote( $parsedURL['host']) . ')?(?:' .
75
+ preg_quote( $parsedURL['path']) . '/)|^)' . $regex_match_file, '/');
76
+ $regex_mysql = str_replace( '(?:', '(', $regex );
77
+ if ( $attachment_id > 0 ) {
78
+ $mediaCount = $wpdb->get_var(
79
+ $wpdb->prepare( "SELECT COUNT(*)
80
+ FROM $wpdb->postmeta
81
+ WHERE post_id != %d
82
+ AND meta_key != '_wp_attached_file'
83
+ AND (meta_value REGEXP %s OR meta_value = %d)",
84
+ $attachment_id, $regex_mysql, $attachment_id
85
+ )
86
+ );
87
+ } else {
88
+ $mediaCount = $wpdb->get_var(
89
+ $wpdb->prepare( "SELECT COUNT(*)
90
+ FROM $wpdb->postmeta
91
+ WHERE meta_key != '_wp_attached_file'
92
+ AND meta_value REGEXP %s",
93
+ $regex_mysql
94
+ )
95
+ );
96
+ }
97
+ if ( $this->core->debug && $mediaCount > 0 )
98
+ error_log("{$file} found in POSTMETA");
99
+ return $mediaCount > 0;
100
+ }
101
+
102
+
103
+ function has_content( $file, $mediaId = null ) {
104
+
105
+ global $wpdb;
106
+ $this->core->last_analysis_ids = null;
107
+ $shortcode_support = get_option( 'wpmc_shortcode', false );
108
+
109
+ // Check in Posts Content
110
+ if ( get_option( 'wpmc_posts', false ) ) {
111
+ $file = $this->core->wpmc_clean_uploaded_filename( $file );
112
+ $uploads = wp_upload_dir();
113
+ $parsedURL = parse_url( $uploads['baseurl'] );
114
+ $pinfo = pathinfo( $file );
115
+ $regex_match_file = '(' . $pinfo['dirname'] . '/' . $pinfo['filename'] . "(\\-[0-9]{1,8}x[0-9]{1,8})?\\." .
116
+ ( isset( $pinfo['extension'] ) ? $pinfo['extension'] : '' ) . ')';
117
+
118
+ // SUPER STRICT MODE
119
+ // $regex = addcslashes('=[\'"](?:(?:http(?:s)?\\:)?//'
120
+ // . preg_quote( $parsedURL['host'] ) . ')?'
121
+ // . preg_quote( $parsedURL['path'] ) . '/'
122
+ // . $regex_match_file . '(?:\\?[^\'"]*)*[\'"]', '/' );
123
+
124
+ // NORMAL REGEX
125
+ $regex = addcslashes( preg_quote( $parsedURL['path']) . '/' . $regex_match_file . '(?:\\?[^\'"]*)*[\'"]', '/' );
126
+ $regex_mysql = str_replace('(?:', '(', $regex);
127
+ $sql = $wpdb->prepare( "SELECT ID
128
+ FROM $wpdb->posts
129
+ WHERE post_type <> 'revision'
130
+ AND post_type <> 'attachment'
131
+ AND post_content REGEXP %s", $regex_mysql );
132
+ $foundIds = $wpdb->get_col( $sql );
133
+ $this->core->last_analysis_ids = $foundIds;
134
+ $mediaCount = count( $foundIds );
135
+ if ( $this->core->debug && $mediaCount > 0 )
136
+ error_log( "File {$file} found in post_content, $mediaCount time(s)" );
137
+ if ( $mediaCount > 0 )
138
+ return true;
139
+
140
+ if ( !empty( $mediaId ) ) {
141
+ $sql = $wpdb->prepare( "SELECT ID
142
+ FROM $wpdb->posts
143
+ WHERE post_type <> 'revision'
144
+ AND post_type <> 'attachment'
145
+ AND post_content LIKE %s", "%wp-image-$mediaId%" );
146
+ $foundIds = $wpdb->get_col( $sql );
147
+ $this->core->last_analysis_ids = $foundIds;
148
+ $mediaCount = count( $foundIds );
149
+ if ( $this->core->debug && $mediaCount > 0 )
150
+ error_log( "Media {$mediaId} found in post_content, $mediaCount time(s)" );
151
+ if ( $mediaCount > 0 )
152
+ return true;
153
+ }
154
+ }
155
+
156
+ // Shortcode analysis
157
+ global $shortcode_tags;
158
+ $active_tags = array_keys( $shortcode_tags );
159
+ if ( !empty( $active_tags ) ) {
160
+ $post_contents = get_transient( 'wpmc_posts_with_shortcode' );
161
+ if ( $post_contents === false ) {
162
+
163
+ $post_contents = array();
164
+
165
+ // Resolve shortcodes from posts
166
+ if ( $shortcode_support ) {
167
+ $query = array();
168
+ $query[] = "SELECT ID, post_content FROM {$wpdb->posts}";
169
+ $query[] = "WHERE post_type <> 'revision' AND post_type <> 'attachment'";
170
+ $sub_query = array();
171
+ foreach ( $active_tags as $tag ) {
172
+ $sub_query[] = "post_content LIKE '%[" . esc_sql( $wpdb->esc_like( $tag ) ) . "%'";
173
+ }
174
+ $query[] = "AND (" . implode ( " OR ", $sub_query ) . ")";
175
+ $sql = join( ' ', $query );
176
+ $results = $wpdb->get_results( $sql );
177
+ foreach ( $results as $key => $data ) {
178
+ $post_contents['post_' . $data->ID] = do_shortcode( $data->post_content );
179
+ }
180
+ }
181
+
182
+ // Read Widgets
183
+ if ( get_option( 'wpmc_widgets', false ) ) {
184
+ global $wp_registered_widgets;
185
+ $active_widgets = get_option( 'sidebars_widgets' );
186
+ foreach ( $active_widgets as $sidebar_name => $sidebar_widgets ) {
187
+ if ( $sidebar_name != 'wp_inactive_widgets' && !empty( $sidebar_widgets ) && is_array( $sidebar_widgets ) ) {
188
+ $i = 0;
189
+ foreach ( $sidebar_widgets as $widget_instance ) {
190
+ $widget_class = $wp_registered_widgets[$widget_instance]['callback'][0]->option_name;
191
+ $instance_id = $wp_registered_widgets[$widget_instance]['params'][0]['number'];
192
+ $widget_data = get_option($widget_class);
193
+ if ( !empty( $widget_data[$instance_id]['text'] ) ) {
194
+
195
+ // Resolve Widgets or just get them
196
+ if ( $shortcode_support )
197
+ $post_contents['widget_' . $i] = do_shortcode( $widget_data[$instance_id]['text'] );
198
+ else
199
+ $post_contents['widget_' . $i] = $widget_data[$instance_id]['text'];
200
+ }
201
+ $i++;
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ if ( !empty( $post_contents ) )
208
+ set_transient( 'wpmc_posts_with_shortcode', $post_contents, 2 * 60 * 60 );
209
+ }
210
+
211
+ if ( !empty( $post_contents ) ) {
212
+ foreach ( $post_contents as $key => $content ) {
213
+ $found = preg_match( '/' . $regex . '/i', $content );
214
+ if ( $this->core->debug && $found )
215
+ error_log( "File Cleaner: {$file} found in {$key} shortcode or widget" );
216
+ if ( $found )
217
+ return true;
218
+ }
219
+ }
220
+ }
221
+ return false;
222
+ }
223
+
224
+ }
225
+
226
+ ?>
trunk/wpmc_custom_checkers.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ ?>
wpmc_admin.php CHANGED
@@ -102,7 +102,6 @@ class Meow_WPMC_Admin extends MeowApps_Admin {
102
  }
103
 
104
  function admin_settings() {
105
- wpmc_check_db()
106
  ?>
107
  <div class="wrap">
108
  <?php
102
  }
103
 
104
  function admin_settings() {
 
105
  ?>
106
  <div class="wrap">
107
  <?php
wpmc_checkers.php CHANGED
@@ -11,22 +11,26 @@ class Meow_WPMC_Checkers {
11
  $this->core = $core;
12
  }
13
 
14
- function wpmc_check_db_has_background_or_header( $file ) {
15
  if ( current_theme_supports( 'custom-header' ) ) {
16
  $custom_header = get_custom_header();
17
  if ( $custom_header && $custom_header->url ) {
18
- if ( strpos( $custom_header->url, $file ) !== false )
19
- if ($this->core->debug) error_log("{$file} found in header");
 
20
  return true;
 
21
  }
22
  }
23
 
24
  if ( current_theme_supports( 'custom-background' ) ) {
25
  $custom_background = get_theme_mod('background_image');
26
  if ( $custom_background ) {
27
- if ( strpos( $custom_background, $file ) !== false )
28
- if ($this->core->debug) error_log("{$file} found in background");
 
29
  return true;
 
30
  }
31
  }
32
 
@@ -56,7 +60,7 @@ class Meow_WPMC_Checkers {
56
  return false;
57
  }
58
 
59
- function wpmc_check_db_has_meta( $file, $attachment_id = 0 ) {
60
 
61
  if ( !get_option( 'wpmc_postmeta', false ) )
62
  return false;
@@ -96,9 +100,10 @@ class Meow_WPMC_Checkers {
96
  }
97
 
98
 
99
- function wpmc_check_db_has_content( $file, $mediaId = null ) {
100
 
101
  global $wpdb;
 
102
  $shortcode_support = get_option( 'wpmc_shortcode', false );
103
 
104
  // Check in Posts Content
@@ -119,24 +124,28 @@ class Meow_WPMC_Checkers {
119
  // NORMAL REGEX
120
  $regex = addcslashes( preg_quote( $parsedURL['path']) . '/' . $regex_match_file . '(?:\\?[^\'"]*)*[\'"]', '/' );
121
  $regex_mysql = str_replace('(?:', '(', $regex);
122
- $sql = $wpdb->prepare( "SELECT COUNT(*)
123
  FROM $wpdb->posts
124
  WHERE post_type <> 'revision'
125
  AND post_type <> 'attachment'
126
  AND post_content REGEXP %s", $regex_mysql );
127
- $mediaCount = $wpdb->get_var( $sql );
 
 
128
  if ( $this->core->debug && $mediaCount > 0 )
129
  error_log( "File {$file} found in post_content, $mediaCount time(s)" );
130
  if ( $mediaCount > 0 )
131
  return true;
132
 
133
  if ( !empty( $mediaId ) ) {
134
- $sql = $wpdb->prepare( "SELECT COUNT(*)
135
  FROM $wpdb->posts
136
  WHERE post_type <> 'revision'
137
  AND post_type <> 'attachment'
138
  AND post_content LIKE %s", "%wp-image-$mediaId%" );
139
- $mediaCount = $wpdb->get_var( $sql );
 
 
140
  if ( $this->core->debug && $mediaCount > 0 )
141
  error_log( "Media {$mediaId} found in post_content, $mediaCount time(s)" );
142
  if ( $mediaCount > 0 )
11
  $this->core = $core;
12
  }
13
 
14
+ function has_background_or_header( $file ) {
15
  if ( current_theme_supports( 'custom-header' ) ) {
16
  $custom_header = get_custom_header();
17
  if ( $custom_header && $custom_header->url ) {
18
+ if ( strpos( $custom_header->url, $file ) !== false ) {
19
+ if ($this->core->debug)
20
+ error_log("{$file} found in header");
21
  return true;
22
+ }
23
  }
24
  }
25
 
26
  if ( current_theme_supports( 'custom-background' ) ) {
27
  $custom_background = get_theme_mod('background_image');
28
  if ( $custom_background ) {
29
+ if ( strpos( $custom_background, $file ) !== false ) {
30
+ if ($this->core->debug)
31
+ error_log("{$file} found in background");
32
  return true;
33
+ }
34
  }
35
  }
36
 
60
  return false;
61
  }
62
 
63
+ function has_meta( $file, $attachment_id = 0 ) {
64
 
65
  if ( !get_option( 'wpmc_postmeta', false ) )
66
  return false;
100
  }
101
 
102
 
103
+ function has_content( $file, $mediaId = null ) {
104
 
105
  global $wpdb;
106
+ $this->core->last_analysis_ids = null;
107
  $shortcode_support = get_option( 'wpmc_shortcode', false );
108
 
109
  // Check in Posts Content
124
  // NORMAL REGEX
125
  $regex = addcslashes( preg_quote( $parsedURL['path']) . '/' . $regex_match_file . '(?:\\?[^\'"]*)*[\'"]', '/' );
126
  $regex_mysql = str_replace('(?:', '(', $regex);
127
+ $sql = $wpdb->prepare( "SELECT ID
128
  FROM $wpdb->posts
129
  WHERE post_type <> 'revision'
130
  AND post_type <> 'attachment'
131
  AND post_content REGEXP %s", $regex_mysql );
132
+ $foundIds = $wpdb->get_col( $sql );
133
+ $this->core->last_analysis_ids = $foundIds;
134
+ $mediaCount = count( $foundIds );
135
  if ( $this->core->debug && $mediaCount > 0 )
136
  error_log( "File {$file} found in post_content, $mediaCount time(s)" );
137
  if ( $mediaCount > 0 )
138
  return true;
139
 
140
  if ( !empty( $mediaId ) ) {
141
+ $sql = $wpdb->prepare( "SELECT ID
142
  FROM $wpdb->posts
143
  WHERE post_type <> 'revision'
144
  AND post_type <> 'attachment'
145
  AND post_content LIKE %s", "%wp-image-$mediaId%" );
146
+ $foundIds = $wpdb->get_col( $sql );
147
+ $this->core->last_analysis_ids = $foundIds;
148
+ $mediaCount = count( $foundIds );
149
  if ( $this->core->debug && $mediaCount > 0 )
150
  error_log( "Media {$mediaId} found in post_content, $mediaCount time(s)" );
151
  if ( $mediaCount > 0 )