PluginProbe
Database Cleaner / 1.3.9
Database Cleaner v1.3.9
1.4.0 1.3.9 1.3.8 1.3.7 1.3.6 0.5.4 0.5.5 0.5.6 0.5.7 0.5.8 0.5.9 0.6.0 0.6.1 0.6.2 0.6.3 0.6.4 0.6.5 0.6.6 0.6.7 0.6.8 0.6.9 0.7.0 0.7.1 0.7.2 0.7.3 All 132 releases
database-cleaner / classes / admin.php

admin.php in Database Cleaner 1.3.9, at classes/admin.php

483 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 class Meow_DBCLNR_Admin extends MeowKit_DBCLNR_Admin {
3 public $core;
4
5 public function __construct( $core ) {
6 parent::__construct( DBCLNR_PREFIX, DBCLNR_ENTRY, DBCLNR_DOMAIN, class_exists( 'MeowPro_DBCLNR_Core' ) );
7 $this->core = $core;
8 if ( is_admin() ) {
9 add_action( 'admin_menu', array( $this, 'app_menu' ) );
10
11 // Load the scripts only if they are needed by the current screen
12 $page = isset( $_GET["page"] ) ? sanitize_text_field( $_GET["page"] ) : null;
13 $is_dbclnr_screen = in_array( $page, [ 'dbclnr_settings', 'dbclnr_dashboard' ] );
14 $is_meowapps_dashboard = $page === 'meowapps-main-menu';
15 if ( $is_meowapps_dashboard || $is_dbclnr_screen ) {
16 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
17 }
18 }
19 }
20
21 function admin_enqueue_scripts() {
22
23 // Load the scripts
24 $physical_file = DBCLNR_PATH . '/app/index.js';
25 $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : DBCLNR_VERSION;
26 wp_register_script( 'dbclnr-vendor', DBCLNR_URL . 'app/vendor.js', ['wp-element', 'wp-i18n'], $cache_buster );
27 wp_register_script( 'dbclnr', DBCLNR_URL . 'app/index.js', ['dbclnr-vendor', 'wp-i18n'], $cache_buster );
28 wp_set_script_translations( 'dbclnr', 'database-cleaner' );
29 wp_enqueue_script( 'dbclnr' );
30
31 // The MD5 of the translation file built by WP uses app/i18n.js instead of app/index.js
32 add_filter( 'load_script_translation_file', function( $file, $handle, $domain ) {
33 if ( $domain !== 'database-cleaner' ) { return $file; }
34 $file = str_replace( md5( 'app/index.js' ), md5( 'app/i18n.js' ), $file );
35 return $file;
36 }, 10, 3 );
37
38 // Localize and options
39 wp_set_script_translations( 'dbclnr', 'database-cleaner' );
40 wp_localize_script( 'dbclnr', 'dbclnr', [
41 'api_url' => rest_url( 'database-cleaner/v1' ),
42 'rest_url' => rest_url(),
43 'plugin_url' => DBCLNR_URL,
44 'prefix' => DBCLNR_PREFIX,
45 'db_prefix' => $this->core->prefix,
46 'domain' => DBCLNR_DOMAIN,
47 'is_pro' => class_exists( 'MeowPro_DBCLNR_Core' ),
48 'is_registered' => !!$this->is_registered(),
49 'has_mcp' => $this->has_mcp(),
50 'rest_nonce' => wp_create_nonce( 'wp_rest' ),
51 'core' => [
52 'posts' => $this->core->add_clean_style_data( Meow_DBCLNR_Items::$POSTS ),
53 'posts_metadata' => $this->core->add_clean_style_data( Meow_DBCLNR_Items::$POSTS_METADATA ),
54 'users' => $this->core->add_clean_style_data( Meow_DBCLNR_Items::$USERS ),
55 'comments' => $this->core->add_clean_style_data( Meow_DBCLNR_Items::$COMMENTS ),
56 'transients' => $this->core->add_clean_style_data( Meow_DBCLNR_Items::$TRANSIENTS ),
57 ],
58 'core_count' => [
59 'posts' => $this->core->get_core_entry_counts( Meow_DBCLNR_Items::$POSTS, true ),
60 'posts_metadata' => $this->core->get_core_entry_counts( Meow_DBCLNR_Items::$POSTS_METADATA, true),
61 'users' => $this->core->get_core_entry_counts( Meow_DBCLNR_Items::$USERS, true ),
62 'comments' => $this->core->get_core_entry_counts( Meow_DBCLNR_Items::$COMMENTS, true ),
63 'transients' => $this->core->get_core_entry_counts( Meow_DBCLNR_Items::$TRANSIENTS, true ),
64 ],
65 'options' => $this->core->get_all_options(),
66 'metadata_tables' => $this->core->get_metadata_tables(),
67 ] );
68 }
69
70 // Runtime only, so it is never persisted with the options.
71 function has_mcp() {
72 global $mwai;
73 return !empty( $mwai ) && method_exists( $mwai, 'hasMCP' ) && $mwai->hasMCP();
74 }
75
76 function is_registered() {
77 return apply_filters( DBCLNR_PREFIX . '_meowapps_is_registered', false, DBCLNR_PREFIX );
78 }
79
80 function app_menu() {
81 add_submenu_page( 'meowapps-main-menu', 'Database Cleaner', 'Database Cleaner', 'manage_options',
82 'dbclnr_settings', array( $this, 'admin_settings' ) );
83 }
84
85 function admin_settings() {
86 echo wp_kses_post( '<div id="dbclnr-admin-settings"></div>' );
87 }
88
89 function get_db_tables() {
90 global $wpdb;
91 $results = $wpdb->get_results( $wpdb->prepare( "
92 SELECT TABLE_NAME 'table'
93 FROM information_schema.TABLES
94 WHERE table_schema = %s
95 ",
96 DB_NAME
97 ), ARRAY_A );
98
99 return $results;
100 }
101
102 function get_options() {
103 global $wpdb;
104 $where = "WHERE option_name NOT LIKE '_transient_%' AND option_name NOT LIKE '_site_transient_%' ";
105 $result = $wpdb->get_results( "
106 SELECT option_name, length(option_value) AS option_value_length, autoload
107 FROM $wpdb->options
108 $where
109 ORDER BY option_value_length DESC
110 ", ARRAY_A);
111
112 return $result;
113 }
114
115 function get_option_value( $option_name ) {
116 global $wpdb;
117 $result = $wpdb->get_results( $wpdb->prepare( "
118 SELECT option_value
119 FROM $wpdb->options
120 WHERE option_name = %s
121 ", $option_name ) );
122 return $result;
123 }
124
125 function delete_options( $option_names ) {
126 global $wpdb;
127 $placeholder = array_fill( 0, count( $option_names ), '%s' );
128 $placeholder = implode( ', ', $placeholder );
129 $result = $wpdb->query( $wpdb->prepare( "
130 DELETE t
131 FROM $wpdb->options t
132 WHERE option_name IN ($placeholder)
133 ", $option_names ) );
134
135 if ($result === false) {
136 throw new Error('Failed to delete the autoloaded options:' . $wpdb->last_error);
137 }
138 return $result;
139 }
140
141 function switch_autoloaded_option( $option_name, $autoload ) {
142 global $wpdb;
143 $result = $wpdb->query( $wpdb->prepare( "
144 UPDATE $wpdb->options
145 SET autoload = %s
146 WHERE option_name = %s
147 ", $autoload, $option_name ) );
148
149 return $result;
150 }
151
152 function get_metadata( $table, $ids = null, $order_by = 'meta_value_length', $order = 'desc',
153 $skip = 0, $limit = 0, $search_list = '', $sizes = [], $postIdList = []
154 ) {
155 global $wpdb;
156 switch ($table) {
157 case $wpdb->postmeta:
158 return $this->get_postmeta( $ids, $order_by, $order, $skip, $limit, $search_list, $sizes, $postIdList );
159 case $wpdb->usermeta:
160 return $this->get_usermeta( $ids, $order_by, $order, $skip, $limit, $search_list, $sizes, $postIdList );
161 }
162 throw new Error("Invalid table specified");
163 }
164
165 function get_metadata_count( $table, $ids = null, $search_list = '', $sizes = [], $postIdList = [] ) {
166 global $wpdb;
167 switch ($table) {
168 case $wpdb->postmeta:
169 return $this->get_postmeta_count( $ids, $search_list, $sizes, $postIdList );
170 case $wpdb->usermeta:
171 return $this->get_usermeta_count( $ids, $search_list, $sizes, $postIdList );
172 }
173 throw new Error("Invalid table specified");
174 }
175
176 function get_postmeta( $ids, $order_by, $order, $skip, $limit, $search_list, $sizes, $postIdList = [] ) {
177 global $wpdb;
178
179 $where_clause = $this->get_metadata_where_clause( $wpdb->postmeta, $ids, $search_list, $sizes, $postIdList );
180 $order_clause = $this->get_metadata_order_clause( $order_by, $order );
181 $limit_clause = $this->get_metadata_limit_clause( $skip, $limit );
182
183 $result = $wpdb->get_results( "
184 SELECT meta_id AS id, post_id, meta_key, length(meta_value) AS meta_value_length, LEFT(meta_value, 15) AS meta_value_preview
185 FROM $wpdb->postmeta
186 $where_clause
187 $order_clause
188 $limit_clause
189 ", ARRAY_A );
190
191 return $result;
192 }
193
194 function get_postmeta_count( $ids, $search_list, $sizes, $postIdList = [] ) {
195 global $wpdb;
196
197 $where_clause = $this->get_metadata_where_clause( $wpdb->postmeta, $ids, $search_list, $sizes, $postIdList );
198
199 $result = (int)$wpdb->get_var( "
200 SELECT COUNT(meta_id)
201 FROM $wpdb->postmeta
202 $where_clause
203 " );
204
205 return $result;
206 }
207
208 function get_usermeta( $ids, $order_by, $order, $skip, $limit, $search_list, $sizes ) {
209 global $wpdb;
210
211 $where_clause = $this->get_metadata_where_clause( $wpdb->usermeta, $ids, $search_list, $sizes );
212 $order_clause = $this->get_metadata_order_clause( $order_by, $order );
213 $limit_clause = $this->get_metadata_limit_clause( $skip, $limit );
214
215 $result = $wpdb->get_results( "
216 SELECT umeta_id AS id, user_id, meta_key, length(meta_value) AS meta_value_length, LEFT(meta_value, 30) AS meta_value_preview
217 FROM $wpdb->usermeta
218 $where_clause
219 $order_clause
220 $limit_clause
221 ", ARRAY_A );
222
223 return $result;
224 }
225
226 function get_usermeta_count( $ids, $search_list, $sizes, $postIdList = [] ) {
227 global $wpdb;
228
229 $where_clause = $this->get_metadata_where_clause( $wpdb->usermeta, $ids, $search_list, $sizes, $postIdList);
230
231 $result = (int)$wpdb->get_var( "
232 SELECT COUNT(umeta_id)
233 FROM $wpdb->usermeta
234 $where_clause
235 " );
236
237 return $result;
238 }
239
240 function get_metadata_order_clause( $order_by, $order ) {
241 $order_clause = 'ORDER BY ';
242 $order_column = 'meta_value_length';
243 $order = $order === 'asc' ? 'ASC' : 'DESC';
244 switch ($order_by) {
245 case 'postId':
246 $order_column = 'post_id';
247 break;
248
249 case 'userId':
250 $order_column = 'user_id';
251 break;
252
253 case 'name':
254 $order_column = 'meta_key';
255 break;
256
257 case 'usedBy':
258 // nothing to do. "usedBy" sort is done in client side.
259 break;
260
261 case 'size':
262 $order_column = 'meta_value_length';
263 break;
264 }
265 return $order_clause . $order_column . ' ' . $order;
266 }
267
268 function get_metadata_limit_clause( $skip, $limit ) {
269 global $wpdb;
270 $limit_clause = '';
271 if ( !empty($limit) ) {
272 $limit_clause = $wpdb->prepare( "LIMIT %d, %d", $skip, $limit );
273 }
274 return $limit_clause;
275 }
276
277 function get_metadata_where_clause( $table, $ids, $search_list, $sizes, $postIdList = [] ) {
278 global $wpdb;
279 $where_clause = 'WHERE 1=1 ';
280 if ( !empty($ids) ) {
281 switch ($table) {
282 case $wpdb->postmeta:
283 $meta_id_column = 'meta_id';
284 break;
285
286 case $wpdb->usermeta:
287 $meta_id_column = 'umeta_id';
288 break;
289 }
290 $placeholder = array_fill( 0, count( $ids ), '%s' );
291 $placeholder = implode( ', ', $placeholder );
292 $where_clause .= $wpdb->prepare( "AND $meta_id_column IN ($placeholder)", $ids );
293 }
294 if ( !empty($search_list) ) {
295 $search_where = [];
296 foreach ( $search_list as $search ) {
297 $search_where[] = $wpdb->prepare( "meta_key LIKE %s", '%' . $search . '%' );
298 }
299 $search_where_clause = implode( ' AND ', $search_where );
300 $where_clause .= "AND ($search_where_clause)";
301 }
302 if ( !empty( $postIdList ) ) {
303 $post_id_where = [];
304 foreach ( $postIdList as $postId ) {
305 $post_id_where[] = $wpdb->prepare( "post_id = %d", (int)$postId );
306 }
307 $post_id_where_clause = implode( ' OR ', $post_id_where );
308 $where_clause .= "AND ($post_id_where_clause)";
309 }
310 if ( count( $sizes ) > 0 ) {
311 $size_where = [];
312 foreach ( $sizes as $size ) {
313 $size_where[] = $wpdb->prepare( "meta_value_length >= %d", (int)$size );
314 }
315 $size_where_clause = implode( ' OR ', $size_where );
316 $where_clause .= "AND ($size_where_clause)";
317 }
318 return $where_clause;
319 }
320
321 function get_metadata_value( $table, $id ) {
322 $postmeta_table = $this->core->prefix . 'postmeta';
323 $usermeta_table = $this->core->prefix . 'usermeta';
324 switch ( $table ) {
325 case $postmeta_table:
326 return $this->get_postmeta_value( $id );
327 case $usermeta_table:
328 return $this->get_usermeta_value( $id );
329 }
330 throw new Error( "Invalid table specified" );
331 }
332
333 function get_postmeta_value( $id ) {
334 global $wpdb;
335 $result = $wpdb->get_results( $wpdb->prepare( "
336 SELECT meta_value
337 FROM $wpdb->postmeta
338 WHERE meta_id = %d
339 ", $id ) );
340 return $result;
341 }
342
343 function get_usermeta_value( $id ) {
344 global $wpdb;
345 $result = $wpdb->get_results( $wpdb->prepare( "
346 SELECT meta_value
347 FROM $wpdb->usermeta
348 WHERE umeta_id = %d
349 ", $id ) );
350 return $result;
351 }
352
353 function delete_metadata( $table, $ids ) {
354 global $wpdb;
355
356 switch ( $table ) {
357 case $wpdb->postmeta:
358 return $this->delete_postmeta( $ids );
359 case $wpdb->usermeta:
360 return $this->delete_usermeta( $ids );
361 }
362
363 throw new Error( "Invalid table specified: $table" );
364 }
365
366 function delete_postmeta( $ids ) {
367 global $wpdb;
368 $placeholder = array_fill( 0, count( $ids ), '%s' );
369 $placeholder = implode( ', ', $placeholder );
370 $result = $wpdb->query( $wpdb->prepare( "
371 DELETE t
372 FROM $wpdb->postmeta t
373 WHERE meta_id IN ($placeholder)
374 ", $ids ) );
375
376 if ($result === false) {
377 throw new Error("Failed to delete the post metadata :" . $wpdb->last_error);
378 }
379 return $result;
380 }
381 function delete_usermeta( $ids ) {
382 global $wpdb;
383 $placeholder = array_fill( 0, count( $ids ), '%s' );
384 $placeholder = implode( ', ', $placeholder );
385 $result = $wpdb->query( $wpdb->prepare( "
386 DELETE t
387 FROM $wpdb->usermeta t
388 WHERE umeta_id IN ($placeholder)
389 ", $ids ) );
390
391 if ($result === false) {
392 throw new Error("Failed to delete the user metadata :" . $wpdb->last_error);
393 }
394 return $result;
395 }
396
397 function delete_crons( $crons ) {
398 foreach ( $crons as $cron ) {
399 $result = $this->core->remove_cron_entry( $cron['name'], $cron['args'] );
400 if ( $result === false ) {
401 throw new Error('Failed to delete the cron option: ' . $cron['name'] );
402 }
403 }
404 return true;
405 }
406
407 function delete_table( $table_name ) {
408 global $wpdb;
409 $result = $wpdb->query( "DROP TABLE IF EXISTS `{$table_name}`;" );
410 if ($result === false) {
411 error_log('PHP Exception: ' . $wpdb->last_error);
412 }
413 return $result;
414 }
415
416 function optimize_table( $table_name ) {
417 global $wpdb;
418 $result = $wpdb->query( "OPTIMIZE TABLE `{$table_name}`;" );
419 if ($result === false) {
420 error_log('PHP Exception: ' . $wpdb->last_error);
421 }
422 return $result;
423 }
424
425 function repair_table( $table_name ) {
426 global $wpdb;
427 $result = $wpdb->query( "OPTIMIZE TABLE `{$table_name}`;" );
428 if ($result === false) {
429 error_log('PHP Exception: ' . $wpdb->last_error);
430 }
431 return $result;
432 }
433
434 function valid_item_operation( $item, $is_auto_clean = false ) {
435 $clean_style = $this->core->get_option( $item . '_clean_style' );
436 if ( $clean_style === 'never' ) {
437 return false;
438 }
439 return !$is_auto_clean || ($is_auto_clean && $clean_style === 'auto');
440 }
441
442 function valid_custom_query_operation( $clean_style, $is_auto_clean = false ) {
443 if ( !$clean_style || $clean_style === 'never') {
444 return false;
445 }
446
447 return !$is_auto_clean || ($is_auto_clean && $clean_style === 'auto');
448 }
449
450 function valid_table_name( $table_name ) {
451 $tables = array_column( $this->get_db_tables(), 'table' );
452 return in_array( $table_name, $tables, true );
453 }
454
455 function valid_deletable_table_name( $table_name ) {
456 // The name arrives already prefixed, and check_table_info() strips the prefix
457 // itself. Prepending it here made the lookup miss every core table, so this
458 // returned true for wp_posts and the likes.
459 $data = apply_filters( 'dbclnr_check_table_info', $table_name, null );
460 return strtolower($data['usedBy']) !== 'wordpress';
461 }
462
463 function valid_deletable_option_name( $option_name ) {
464 $data = apply_filters( 'dbclnr_check_option_info', $option_name, null );
465 return strtolower($data['usedBy']) !== 'wordpress';
466 }
467
468 function valid_deletable_cron_name( $option_name ) {
469 $data = apply_filters( 'dbclnr_check_cron_info', $option_name, null );
470 return strtolower($data['usedBy']) !== 'wordpress';
471 }
472
473 function valid_deletable_metadata( $metadata_key ) {
474 $data = apply_filters( 'dbclnr_check_metadata_info', $metadata_key, null );
475 return strtolower($data['usedBy']) !== 'wordpress';
476 }
477
478 function valid_metadata_table( $table ) {
479 return $table && in_array( $table, $this->core->get_metadata_tables(), true );
480 }
481 }
482
483 ?>