Media Cleaner - Version 5.6.3

Version Description

  • Fix: There was an issue with the "Ignore" feature which was not working in some cases.
  • Add: Filter to allow developers to override the decisions of the plugin.
  • Add: Auto-add MEDIA_TRASH.
  • Fix: Fuzzier pattern matching for wording variety.
  • Info: This plugin is a lot of work. Please help me by giving it a nice review, here.
Download this release

Release Info

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

Code changes from version 5.6.2 to 5.6.3

Files changed (8) hide show
  1. api.php +130 -0
  2. core.php +59 -25
  3. media-cleaner.php +2 -2
  4. readme.txt +8 -2
  5. scripts/dashboard.js +61 -0
  6. scripts/style.css +13 -1
  7. ui.php +9 -3
  8. views/menu-screen.php +5 -3
api.php CHANGED
@@ -1,6 +1,18 @@
1
  <?php
2
 
3
  class Meow_WPMC_API {
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  /**
6
  * @param Meow_WPMC_Core $core
@@ -11,6 +23,7 @@ class Meow_WPMC_API {
11
  $this->core = $core;
12
  $this->engine = $engine;
13
  $this->admin = $admin;
 
14
  add_action( 'wp_ajax_wpmc_get_num_posts', array( $this, 'wp_ajax_wpmc_get_num_posts' ) );
15
  add_action( 'wp_ajax_wpmc_extract_references', array( $this, 'wp_ajax_wpmc_extract_references' ) );
16
  add_action( 'wp_ajax_wpmc_retrieve_targets', array( $this, 'wp_ajax_wpmc_retrieve_targets' ) );
@@ -27,6 +40,123 @@ class Meow_WPMC_API {
27
  * ASYNCHRONOUS AJAX FUNCTIONS
28
  ******************************************************************************/
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  /**
31
  * Method: POST
32
  * Params:
1
  <?php
2
 
3
  class Meow_WPMC_API {
4
+ private $core;
5
+ private $admin;
6
+ private $engine;
7
+
8
+ // Error code enums
9
+ // Should be able to referred from the JS client
10
+ const E = array (
11
+ 'INVALID_NONCE' => 1,
12
+ 'INVALID_PARAMS' => 2,
13
+ 'FILE_OPEN_FAILURE' => 10,
14
+ 'FILE_WRITE_FAILURE' => 11
15
+ );
16
 
17
  /**
18
  * @param Meow_WPMC_Core $core
23
  $this->core = $core;
24
  $this->engine = $engine;
25
  $this->admin = $admin;
26
+ add_action( 'wp_ajax_wpmc_define', array( $this, 'wp_ajax_wpmc_define' ) );
27
  add_action( 'wp_ajax_wpmc_get_num_posts', array( $this, 'wp_ajax_wpmc_get_num_posts' ) );
28
  add_action( 'wp_ajax_wpmc_extract_references', array( $this, 'wp_ajax_wpmc_extract_references' ) );
29
  add_action( 'wp_ajax_wpmc_retrieve_targets', array( $this, 'wp_ajax_wpmc_retrieve_targets' ) );
40
  * ASYNCHRONOUS AJAX FUNCTIONS
41
  ******************************************************************************/
42
 
43
+ /**
44
+ * Writes a constant definition on wp-config.php
45
+ *
46
+ * Method: POST
47
+ * Params:
48
+ * - nonce: Nonce
49
+ * - name: Name of the constant to define
50
+ * - value: Value of the constant
51
+ * Return:
52
+ * - data.action: 'wpmc_define'
53
+ * - data.message: <string> (on failure)
54
+ * - data.code: <int> (on failure)
55
+ */
56
+ function wp_ajax_wpmc_define() {
57
+ try {
58
+ $action = 'wpmc_define';
59
+ $nonce = isset( $_POST['nonce'] ) ? $_POST['nonce'] : '';
60
+ if ( !wp_verify_nonce( $nonce, $action ) ) throw new Exception( "Nonce check failed", self::E['INVALID_NONCE'] );
61
+
62
+ $name = isset( $_POST['name'] ) ? $_POST['name'] : '';
63
+ $value = isset( $_POST['value'] ) ? $_POST['value'] : '';
64
+ $result = array (
65
+ 'success' => false,
66
+ 'data' => array ( 'action' => $action )
67
+ );
68
+
69
+ /* Check the constant name and type */
70
+ $supported = false;
71
+ $constants = array ( // Supported constants
72
+ /* We can add more constants to support */
73
+ 'MEDIA_TRASH' => array ( 'type' => 'boolean' )
74
+ );
75
+ foreach ( $constants as $i => $item ) {
76
+ if ( $i != $name ) continue;
77
+ $supported = true;
78
+ /* Transform the value into a proper format */
79
+ switch ( $item['type'] ) {
80
+ case 'boolean':
81
+ case 'bool':
82
+ $value = boolval( $value ) ? 'true' : 'false';
83
+ break;
84
+ case 'integer':
85
+ case 'int':
86
+ $value = intval( $value );
87
+ break;
88
+ case 'string':
89
+ $value = "'" . strval( $value ) . "'";
90
+ break;
91
+ }
92
+ break;
93
+ }
94
+ if ( !$supported ) throw new Exception( "Invalid parameters", self::E['INVALID_PARAMS'] );
95
+
96
+ /* Open wp-config.php */
97
+ $conf = ABSPATH . 'wp-config.php';
98
+ $stream = fopen( $conf, 'r+' );
99
+ if ( $stream === false ) throw new Exception( "Failed to open the config file", self::E['FILE_OPEN_FAILURE'] );
100
+
101
+ try {
102
+ if ( !flock( $stream, LOCK_EX ) ) throw new Exception( "Failed to lock the config file", self::E['FILE_OPEN_FAILURE'] );
103
+ $stat = fstat( $stream );
104
+
105
+ /* Find out the ideal position to write on */
106
+ $found = false;
107
+ $patterns = array (
108
+ array (
109
+ 'regex' => '^\/\*\s*' . preg_quote( "That's all, stop editing!" ) . '.*?\s*\*\/',
110
+ 'where' => 'above'
111
+ )
112
+ );
113
+ $current = 0;
114
+ while ( !feof( $stream ) ) {
115
+ $line = fgets( $stream ); // Read line by line
116
+ if ( $line === false ) break; // No more lines
117
+ $prev = $current; // Previous position
118
+ $current = ftell( $stream ); // Current position
119
+
120
+ foreach ( $patterns as $item ) {
121
+ if ( !preg_match( '/'.$item['regex'].'/', trim( $line ) ) ) continue;
122
+ /* Found */
123
+ $found = true;
124
+ if ( $item['where'] == 'above' ) {
125
+ fseek( $stream, $prev );
126
+ $current = $prev;
127
+ }
128
+ break 2;
129
+ }
130
+ }
131
+
132
+ /* Check if the position is found */
133
+ if ( !$found ) throw new Exception( "Cannot determine the position to write on", self::E['FILE_WRITE_FAILURE'] );
134
+
135
+ /* Write the constant definition line */
136
+ $new = "define( '{$name}', {$value} );" . PHP_EOL;
137
+ $rest = fread( $stream, $stat['size'] - $current );
138
+ fseek( $stream, $current );
139
+ $written = fwrite( $stream, $new . $rest );
140
+
141
+ /* All done */
142
+ if ( $written === false ) throw new Exception( "Failed to write on the config file", self::E['FILE_WRITE_FAILURE'] );
143
+ fclose( $stream );
144
+
145
+ } catch( Exception $e ) {
146
+ fclose( $stream );
147
+ throw $e;
148
+ }
149
+
150
+ } catch( Exception $e ) {
151
+ $result['data']['message'] = $e->getMessage();
152
+ $result['data']['code'] = $e->getCode();
153
+ exit( json_encode( $result ) );
154
+ }
155
+
156
+ $result['success'] = true;
157
+ exit( json_encode( $result ) );
158
+ }
159
+
160
  /**
161
  * Method: POST
162
  * Params:
core.php CHANGED
@@ -834,47 +834,81 @@ class Meow_WPMC_Core {
834
  return $paths;
835
  }
836
 
 
 
 
 
 
 
 
 
 
 
 
837
  function check_media( $attachmentId, $checkOnly = false ) {
 
 
 
 
 
 
838
  $size = 0;
839
  $countfiles = 0;
840
  $check_broken_media = !$this->check_content;
841
  $fullpath = get_attached_file( $attachmentId );
842
  $is_broken = !file_exists( $fullpath );
843
 
844
- if ( $check_broken_media && !$is_broken )
845
- return true;
 
 
 
846
 
 
847
  $issue = 'NO_CONTENT';
848
  $paths = $this->get_paths_from_attachment( $attachmentId );
849
  foreach ( $paths as $path ) {
850
- if ( $this->check_content && $this->reference_exists( $path, $attachmentId ) )
851
- return true;
 
 
 
 
 
 
 
 
852
  $filepath = trailingslashit( $this->upload_folder['basedir'] ) . $path;
853
  if ( file_exists( $filepath ) )
854
  $size += filesize( $filepath );
855
  $countfiles++;
856
  }
857
 
858
- if ( $is_broken ) {
859
- $this->log( "File {$fullpath} does not exist." );
860
- $issue = 'ORPHAN_MEDIA';
861
- }
862
- if ( !$checkOnly ) {
863
- global $wpdb;
864
- $table_name = $wpdb->prefix . "mclean_scan";
865
- $mainfile = $this->clean_uploaded_filename( $fullpath );
866
- $wpdb->insert( $table_name,
867
- array(
868
- 'time' => current_time('mysql'),
869
- 'type' => 1,
870
- 'size' => $size,
871
- 'path' => $mainfile . ( $countfiles > 0 ? ( " (+ " . $countfiles . " files)" ) : "" ),
872
- 'postId' => $attachmentId,
873
- 'issue' => $issue
874
- )
875
- );
 
 
 
 
 
876
  }
877
- return false;
878
  }
879
 
880
  // Delete all issues
@@ -936,8 +970,8 @@ function wpmc_reset () {
936
  }
937
 
938
  function wpmc_uninstall () {
939
- wpmc_remove_options();
940
- wpmc_remove_database();
941
  }
942
 
943
  function wpmc_create_database() {
834
  return $paths;
835
  }
836
 
837
+ function is_media_ignored( $attachmentId ) {
838
+ global $wpdb;
839
+ $table_name = $wpdb->prefix . "mclean_scan";
840
+ $issue = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE postId = %d", $attachmentId ), OBJECT );
841
+ error_log( $attachmentId );
842
+ error_log( print_r( $issue, 1 ) );
843
+ if ( $issue && $issue->ignored )
844
+ return true;
845
+ return false;
846
+ }
847
+
848
  function check_media( $attachmentId, $checkOnly = false ) {
849
+
850
+ // Is Media ID ignored, consider as used.
851
+ if ( $this->is_media_ignored( $attachmentId ) ) {
852
+ return true;
853
+ }
854
+
855
  $size = 0;
856
  $countfiles = 0;
857
  $check_broken_media = !$this->check_content;
858
  $fullpath = get_attached_file( $attachmentId );
859
  $is_broken = !file_exists( $fullpath );
860
 
861
+ // It's a broken-only scan
862
+ if ( $check_broken_media && !$is_broken ) {
863
+ $is_considered_used = apply_filters( 'wpmc_check_media', true, $attachmentId, false );
864
+ return $is_considered_used;
865
+ }
866
 
867
+ // Let's analyze the usage of each path (thumbnails included) for this Media ID.
868
  $issue = 'NO_CONTENT';
869
  $paths = $this->get_paths_from_attachment( $attachmentId );
870
  foreach ( $paths as $path ) {
871
+
872
+ // If it's found in the content, we stop the scan right away
873
+ if ( $this->check_content && $this->reference_exists( $path, $attachmentId ) ) {
874
+ $is_considered_used = apply_filters( 'wpmc_check_media', true, $attachmentId, false );
875
+ if ( $is_considered_used ) {
876
+ return true;
877
+ }
878
+ }
879
+
880
+ // Let's count the size of the files for later, in case it's unused
881
  $filepath = trailingslashit( $this->upload_folder['basedir'] ) . $path;
882
  if ( file_exists( $filepath ) )
883
  $size += filesize( $filepath );
884
  $countfiles++;
885
  }
886
 
887
+ // This Media ID seems not in used (or broken)
888
+ // Let's double-check through the filter (overridable by users)
889
+ $is_considered_used = apply_filters( 'wpmc_check_media', false, $attachmentId, $is_broken );
890
+ if ( !$is_considered_used ) {
891
+ if ( $is_broken ) {
892
+ $this->log( "File {$fullpath} does not exist." );
893
+ $issue = 'ORPHAN_MEDIA';
894
+ }
895
+ if ( !$checkOnly ) {
896
+ global $wpdb;
897
+ $table_name = $wpdb->prefix . "mclean_scan";
898
+ $mainfile = $this->clean_uploaded_filename( $fullpath );
899
+ $wpdb->insert( $table_name,
900
+ array(
901
+ 'time' => current_time('mysql'),
902
+ 'type' => 1,
903
+ 'size' => $size,
904
+ 'path' => $mainfile . ( $countfiles > 0 ? ( " (+ " . $countfiles . " files)" ) : "" ),
905
+ 'postId' => $attachmentId,
906
+ 'issue' => $issue
907
+ )
908
+ );
909
+ }
910
  }
911
+ return $is_considered_used;
912
  }
913
 
914
  // Delete all issues
970
  }
971
 
972
  function wpmc_uninstall () {
973
+ //wpmc_remove_options();
974
+ //wpmc_remove_database();
975
  }
976
 
977
  function wpmc_create_database() {
media-cleaner.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Media Cleaner
4
  Plugin URI: https://meowapps.com
5
  Description: Clean your Media Library, many options, trash system.
6
- Version: 5.6.2
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: media-cleaner
@@ -25,7 +25,7 @@ if ( is_admin() ) {
25
 
26
  global $wpmc_version;
27
  global $wpmc;
28
- $wpmc_version = '5.6.2';
29
 
30
  require __DIR__ . '/admin.php';
31
  require __DIR__ . '/core.php';
3
  Plugin Name: Media Cleaner
4
  Plugin URI: https://meowapps.com
5
  Description: Clean your Media Library, many options, trash system.
6
+ Version: 5.6.3
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: media-cleaner
25
 
26
  global $wpmc_version;
27
  global $wpmc;
28
+ $wpmc_version = '5.6.3';
29
 
30
  require __DIR__ . '/admin.php';
31
  require __DIR__ . '/core.php';
readme.txt CHANGED
@@ -5,7 +5,7 @@ Donate link: https://commerce.coinbase.com/checkout/d047546a-77a8-41c8-9ea9-4a95
5
  Requires at least: 4.8
6
  Tested up to: 5.3
7
  Requires PHP: 7.0
8
- Stable tag: 5.6.2
9
 
10
  Clean your WordPress from unused or broken media and files. It has its own trash system and recovery features. Please read the description.
11
 
@@ -74,11 +74,17 @@ Better to be safe than sorry. This plugin deletes files! Therefore, backup is no
74
 
75
  == Changelog ==
76
 
 
 
 
 
 
 
 
77
  = 5.6.2 =
78
  * Add: Always Skip/Retry feature.
79
  * Add: "Images Only" for Media Library scan.
80
  * Add: Support for Salient theme.
81
- * Info: This plugin is a lot of work. Please help me by giving it a nice review, [here](https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post).
82
 
83
  = 5.6.1 =
84
  * Add: You can now sort the results by size and path. Little present for the week-end ;)
5
  Requires at least: 4.8
6
  Tested up to: 5.3
7
  Requires PHP: 7.0
8
+ Stable tag: 5.6.3
9
 
10
  Clean your WordPress from unused or broken media and files. It has its own trash system and recovery features. Please read the description.
11
 
74
 
75
  == Changelog ==
76
 
77
+ = 5.6.3 =
78
+ * Fix: There was an issue with the "Ignore" feature which was not working in some cases.
79
+ * Add: Filter to allow developers to override the decisions of the plugin.
80
+ * Add: Auto-add MEDIA_TRASH.
81
+ * Fix: Fuzzier pattern matching for wording variety.
82
+ * Info: This plugin is a lot of work. Please help me by giving it a nice review, [here](https://wordpress.org/support/plugin/media-cleaner/reviews/?rate=5#new-post).
83
+
84
  = 5.6.2 =
85
  * Add: Always Skip/Retry feature.
86
  * Add: "Images Only" for Media Library scan.
87
  * Add: Support for Salient theme.
 
88
 
89
  = 5.6.1 =
90
  * Add: You can now sort the results by size and path. Little present for the week-end ;)
scripts/dashboard.js CHANGED
@@ -4,6 +4,13 @@ Description: Clean your Media Library and Uploads Folder.
4
  Author: Jordy Meow
5
  */
6
 
 
 
 
 
 
 
 
7
  const WPMC_TARGET_FILES = 'files';
8
  const WPMC_TARGET_MEDIAS = 'media';
9
  const WPMC_SOURCE_CONTENT = 'content';
@@ -820,6 +827,60 @@ ErrorHandler.prototype.handle = function (msg = null, status = null) {
820
  )
821
  });
822
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
  })();
824
 
825
  // Dialog
4
  Author: Jordy Meow
5
  */
6
 
7
+ if (WPMC_E) { // WPMC_E is supposed to be passed from PHP with wp_localize_script()
8
+ // Since wp_localize_script() forces any values to be strings,
9
+ // we need to cast these error-codes back into integers.
10
+ // This issue will be fixed in a future version of WP.
11
+ for (code in WPMC_E) WPMC_E[code] = parseInt(WPMC_E[code]);
12
+ }
13
+
14
  const WPMC_TARGET_FILES = 'files';
15
  const WPMC_TARGET_MEDIAS = 'media';
16
  const WPMC_SOURCE_CONTENT = 'content';
827
  )
828
  });
829
  });
830
+
831
+ // "Enable MEDIA_TRASH" Button
832
+ $('#wpmc_enable_media_trash').on('click', function (ev) {
833
+ var self = $(this);
834
+ var action = 'wpmc_define';
835
+
836
+ $.ajax(ajaxurl, {
837
+ type: 'POST',
838
+ dataType: 'text',
839
+ data: {
840
+ action: action,
841
+ nonce: WPMC_NONCES[action],
842
+ name: 'MEDIA_TRASH',
843
+ value: 1
844
+ }
845
+
846
+ }).done(function (result) {
847
+ result = wpmc_parse_response(result);
848
+ if (!result.success) {
849
+ console.error(result.data.message);
850
+ var msg = {
851
+ title: 'Warning',
852
+ head: '',
853
+ body: result.data.message
854
+ };
855
+ switch (result.data.code) {
856
+ case WPMC_E['INVALID_NONCE']:
857
+ msg.head = "Invalid Request";
858
+ msg.body = "Something wrong is going on here.<br>Please try it again after reloading the page";
859
+ break;
860
+ case WPMC_E['FILE_OPEN_FAILURE']:
861
+ msg.head = "Cannot open wp-config.php";
862
+ msg.body = "You still have a chance to enable it by manually editing wp-config.php";
863
+ break;
864
+ case WPMC_E['FILE_WRITE_FAILURE']:
865
+ msg.head = "Cannot modify wp-config.php";
866
+ msg.body = "You still have a chance to enable it by manually editing wp-config.php";
867
+ break;
868
+ }
869
+ wpmc_open_dialog(msg);
870
+ return;
871
+ }
872
+ self.closest('.notice').remove();
873
+
874
+ }).fail(function (e) {
875
+ console.error(e.status + " " + e.statusText);
876
+ var msg = {
877
+ title: "Request Error",
878
+ head: e.status + " " + e.statusText,
879
+ body: "You still have a chance to enable it by manually editing wp-config.php"
880
+ };
881
+ wpmc_open_dialog(msg);
882
+ });
883
+ });
884
  })();
885
 
886
  // Dialog
scripts/style.css CHANGED
@@ -75,4 +75,16 @@
75
  }
76
  .wrap-media-cleaner .notice:first-of-type {
77
  margin: 10px 0px 15px;
78
- }
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
76
  .wrap-media-cleaner .notice:first-of-type {
77
  margin: 10px 0px 15px;
78
+ }
79
+ .wrap-media-cleaner .notice.columned {
80
+ display: flex;
81
+ flex-direction: row;
82
+ }
83
+ .wrap-media-cleaner .notice.columned > *:first-child {
84
+ flex: auto;
85
+ margin-right: 12px;
86
+ }
87
+ .wrap-media-cleaner .notice.columned > *:last-child {
88
+ margin: 12px 0;
89
+ padding-left: 12px;
90
+ }
ui.php CHANGED
@@ -75,16 +75,22 @@ class Meow_WPMC_UI {
75
  }
76
 
77
  function wp_enqueue_scripts() {
 
78
  wp_enqueue_style( 'wp-jquery-ui-dialog' );
79
  wp_enqueue_script( 'jquery-ui-dialog' );
80
  wp_enqueue_style( 'media-cleaner-css', plugins_url( '/scripts/style.css', __FILE__ ) );
81
 
82
  $screen = get_current_screen();
83
- global $wpmc_version;
84
  switch ( $screen->id ) {
85
  case 'media_page_media-cleaner': // Media > Cleaner
86
- wp_enqueue_script( 'media-cleaner', plugins_url( '/scripts/dashboard.js', __FILE__ ), array( 'jquery', 'jquery-ui-dialog' ),
87
- $wpmc_version, true );
 
 
 
 
 
 
88
  break;
89
  case 'meow-apps_page_wpmc_settings-menu': // Meow Apps > Media Cleaner (Settings)
90
  wp_enqueue_script( 'media-cleaner-settings', plugins_url( '/scripts/settings.js', __FILE__ ), array( 'jquery' ),
75
  }
76
 
77
  function wp_enqueue_scripts() {
78
+ global $wpmc_version;
79
  wp_enqueue_style( 'wp-jquery-ui-dialog' );
80
  wp_enqueue_script( 'jquery-ui-dialog' );
81
  wp_enqueue_style( 'media-cleaner-css', plugins_url( '/scripts/style.css', __FILE__ ) );
82
 
83
  $screen = get_current_screen();
 
84
  switch ( $screen->id ) {
85
  case 'media_page_media-cleaner': // Media > Cleaner
86
+ $handle = 'media-cleaner';
87
+ wp_enqueue_script( $handle, plugins_url( '/scripts/dashboard.js', __FILE__ ), array( 'jquery', 'jquery-ui-dialog' ), $wpmc_version, true );
88
+
89
+ $actions = array ( 'wpmc_define' );
90
+ $nonces = array (); // action => nonce
91
+ foreach ( $actions as $item ) $nonces[$item] = wp_create_nonce( $item );
92
+ wp_localize_script( $handle, 'WPMC_NONCES', $nonces );
93
+ wp_localize_script( $handle, 'WPMC_E', Meow_WPMC_API::E ); // Error code enums
94
  break;
95
  case 'meow-apps_page_wpmc_settings-menu': // Meow Apps > Media Cleaner (Settings)
96
  wp_enqueue_script( 'media-cleaner-settings', plugins_url( '/scripts/settings.js', __FILE__ ), array( 'jquery' ),
views/menu-screen.php CHANGED
@@ -173,9 +173,11 @@
173
  }
174
 
175
  if ( !MEDIA_TRASH ) {
176
- echo "<div class='notice notice-warning'>";
177
- _e( "<p>The trash for the Media Library is disabled. Any media removed by the plugin will be <b>permanently deleted</b>. To enable it, modify your wp-config.php file and add this line (preferably at the top): <b>define( 'MEDIA_TRASH', true );</b>", 'media-cleaner' );
178
- echo "</p></div>";
 
 
179
  }
180
  }
181
 
173
  }
174
 
175
  if ( !MEDIA_TRASH ) {
176
+ echo "<div class='notice notice-warning columned'>";
177
+ _e( "<p>The trash for the Media Library is disabled. Any media removed by the plugin will be <b>permanently deleted</b>. To enable it, modify your wp-config.php file and add this line (preferably at the top): <b>define( 'MEDIA_TRASH', true );</b>", 'media-cleaner' );
178
+ echo '</p>';
179
+ echo '<div><a href="#" id="wpmc_enable_media_trash" class="button-primary">'. __( 'Add it automatically', 'media-cleaner' ) .'</a></div>';
180
+ echo '</div>';
181
  }
182
  }
183