$max_characters){ $new_name = trim(substr($new_name, 0, $max_characters)); $new_name = $new_name . " " . "..." . "" . esc_html($string_value) . ""; } return $new_name; } /** Reduces the value of the string in parameter according to max length then create a tooltip for it */ function aDBc_create_tooltip_by_replace($string_value, $max_characters, $tooltip_content){ $new_name = $string_value; if(strlen($new_name) > $max_characters){ $new_name = substr($new_name, 0, $max_characters) . " ..."; } $new_name = "" . esc_html($new_name) . "" . esc_html($tooltip_content) . ""; return $new_name; } /** Reduces the value of the option value according to max length then create a tooltip for it */ function aDBc_create_tooltip_for_option_value($string_value, $max_characters){ $option_content = maybe_unserialize($string_value); if(is_array($option_content)){ $new_name = "Array" . " " . "..." . "" . esc_html($string_value) . ""; }else if(gettype($option_content) == 'object'){ $new_name = "Object" . " " . "..." . "" . esc_html($string_value) . ""; }else{ $new_name = esc_html($string_value); if(strlen($new_name) > $max_characters){ $new_name = trim(substr($new_name, 0, $max_characters)); $new_name = $new_name . " " . "..." . "" . esc_html($string_value) . ""; } } return $new_name; } function aDBc_get_order_by_sql_arg($default_order_by){ // Prepare ORDER BY and ORDER $order_by = " ORDER BY " . $default_order_by . " ASC"; if(!empty($_GET['orderby'])){ $sanitized_orderby = preg_replace('/[^a-zA-Z0-9_\.]/', '', $_GET['orderby']); $order_by = " ORDER BY " . $sanitized_orderby; if(!empty($_GET['order'])){ $order_by .= 'DESC' === strtoupper( $_GET['order'] ) ? ' DESC' : ' ASC'; } else { $order_by .= " ASC"; } } return $order_by; } function aDBc_get_limit_offset_sql_args(){ // Identify current page for WP_List_Table $page_number = 1; if(!empty($_GET['paged'])){ $page_number = absint($_GET['paged']); } // Identify items per page to display $per_page = 50; if(!empty($_GET['per_page'])){ $per_page = absint($_GET['per_page']); } // Prepare LIMIT and OFFSET $offset = ($page_number - 1) * $per_page; $limit_offset = " LIMIT $offset,$per_page"; return $limit_offset; } /** Cleans all elements in the current site and in MU according to the selected type */ function aDBc_clean_all_elements_type($type){ global $wpdb; if(function_exists('is_multisite') && is_multisite()){ $blogs_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach($blogs_ids as $blog_id){ switch_to_blog($blog_id); aDBc_clean_elements_type($type); restore_current_blog(); } }else{ aDBc_clean_elements_type($type); } } /** Cleans all elements in the current site according to the selected type */ function aDBc_clean_elements_type($type){ global $wpdb; switch($type){ case "revision": $revision_date = aDBc_get_keep_last_sql_arg('revision','post_modified'); $wpdb->query("DELETE FROM $wpdb->posts WHERE post_type = 'revision'" . $revision_date); break; case "auto-draft": $auto_draft_date = aDBc_get_keep_last_sql_arg('auto-draft','post_modified'); $wpdb->query("DELETE FROM $wpdb->posts WHERE post_status = 'auto-draft'" . $auto_draft_date); break; case "trash-posts": $trash_post_date = aDBc_get_keep_last_sql_arg('trash-posts','post_modified'); $wpdb->query("DELETE FROM $wpdb->posts WHERE post_status = 'trash'" . $trash_post_date); break; case "moderated-comments": $moderated_comment_date = aDBc_get_keep_last_sql_arg('moderated-comments','comment_date'); $wpdb->query("DELETE FROM $wpdb->comments WHERE comment_approved = '0'" . $moderated_comment_date); break; case "spam-comments": $spam_comment_date = aDBc_get_keep_last_sql_arg('spam-comments','comment_date'); $wpdb->query("DELETE FROM $wpdb->comments WHERE comment_approved = 'spam'" . $spam_comment_date); break; case "trash-comments": $trash_comment_date = aDBc_get_keep_last_sql_arg('trash-comments','comment_date'); $wpdb->query("DELETE FROM $wpdb->comments WHERE comment_approved = 'trash'" . $trash_comment_date); break; case "pingbacks": $pingback_date = aDBc_get_keep_last_sql_arg('pingbacks','comment_date'); $wpdb->query("DELETE FROM $wpdb->comments WHERE comment_type = 'pingback'" . $pingback_date); break; case "trackbacks": $trackback_date = aDBc_get_keep_last_sql_arg('trackbacks','comment_date'); $wpdb->query("DELETE FROM $wpdb->comments WHERE comment_type = 'trackback'" . $trackback_date); break; case "orphan-postmeta": $wpdb->query("DELETE pm FROM $wpdb->postmeta pm LEFT JOIN $wpdb->posts wp ON wp.ID = pm.post_id WHERE wp.ID IS NULL"); break; case "orphan-commentmeta": $wpdb->query("DELETE FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_id FROM $wpdb->comments)"); break; case "orphan-relationships": $wpdb->query("DELETE FROM $wpdb->term_relationships WHERE term_taxonomy_id=1 AND object_id NOT IN (SELECT id FROM $wpdb->posts)"); break; case "orphan-usermeta": $wpdb->query("DELETE FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)"); break; case "orphan-termmeta": $wpdb->query("DELETE FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)"); break; case "expired-transients": $type_arg = " AND b.option_value < UNIX_TIMESTAMP()"; aDBc_clean_all_transients($type_arg); break; } } /** Cleans transients based on the type specified in parameter */ function aDBc_clean_all_transients($type_arg){ global $wpdb; $aDBc_transients = $wpdb->get_results("SELECT a.option_name, b.option_value FROM $wpdb->options a LEFT JOIN $wpdb->options b ON b.option_name = CONCAT( CASE WHEN a.option_name LIKE '_site_transient_%' THEN '_site_transient_timeout_' ELSE '_transient_timeout_' END , SUBSTRING(a.option_name, CHAR_LENGTH( CASE WHEN a.option_name LIKE '_site_transient_%' THEN '_site_transient_' ELSE '_transient_' END ) + 1) ) WHERE (a.option_name LIKE '_transient_%' OR a.option_name LIKE '_site_transient_%') AND a.option_name NOT LIKE '%_transient_timeout_%'" . $type_arg); foreach($aDBc_transients as $transient){ $site_wide = (strpos($transient->option_name, '_site_transient') !== false); $name = str_replace($site_wide ? '_site_transient_' : '_transient_', '', $transient->option_name); if(false !== $site_wide){ delete_site_transient($name); }else{ delete_transient($name); } } } /** Cleans scheduled elements in the current site and in MU (used by the scheduler) */ function aDBc_clean_scheduled_elements($schedule_name){ global $wpdb; $schedule_settings = get_option('aDBc_clean_schedule'); if(is_array($schedule_settings) && array_key_exists($schedule_name, $schedule_settings)){ $schedule_params = $schedule_settings[$schedule_name]; $elements_to_clean = $schedule_params['elements_to_clean']; if(function_exists('is_multisite') && is_multisite()){ $blogs_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach($blogs_ids as $blog_id){ switch_to_blog($blog_id); foreach($elements_to_clean as $type){ aDBc_clean_elements_type($type); } restore_current_blog(); } }else{ foreach($elements_to_clean as $type){ aDBc_clean_elements_type($type); } } // After clean up, verify if the caller is a "ONCE" schedule, if so, change its settings in DB to inactive... if($schedule_params['repeat'] == "once"){ $schedule_params['active'] = "0"; $schedule_settings[$schedule_name] = $schedule_params; update_option('aDBc_clean_schedule', $schedule_settings, "no"); } } } /** Optimizes/repairs all tables having lost space or that should be repaired (used by the scheduler) */ function aDBc_optimize_scheduled_tables($schedule_name){ global $wpdb; $schedule_settings = get_option('aDBc_optimize_schedule'); if(is_array($schedule_settings) && array_key_exists($schedule_name, $schedule_settings)){ $schedule_params = $schedule_settings[$schedule_name]; $operations = $schedule_params['operations']; // Perform optimize operation if(in_array("optimize", $operations)){ $result = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE table_schema = '" . DB_NAME ."' and Engine <> 'InnoDB' and data_free > 0"); foreach($result as $table){ // Get table name $table_name = ""; // This test to prevent issues in MySQL 8 where tables are not shown // MySQL 5 uses $table->table_name while MySQL 8 uses $table->TABLE_NAME if(property_exists($table, "table_name")){ $table_name = $table->table_name; }else if(property_exists($table, "TABLE_NAME")){ $table_name = $table->TABLE_NAME; } $wpdb->query("OPTIMIZE TABLE " . $table_name); } } // Perform repair operation if(in_array("repair", $operations)){ $result = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE table_schema = '" . DB_NAME ."' and Engine IN ('CSV', 'MyISAM', 'ARCHIVE')"); foreach($result as $table){ // Get table name $table_name = ""; // This test to prevent issues in MySQL 8 where tables are not shown // MySQL 5 uses $table->table_name while MySQL 8 uses $table->TABLE_NAME if(property_exists($table, "table_name")){ $table_name = $table->table_name; }else if(property_exists($table, "TABLE_NAME")){ $table_name = $table->TABLE_NAME; } $query_result = $wpdb->get_results("CHECK TABLE " . $table_name); foreach($query_result as $row){ if($row->Msg_type == 'error'){ if(preg_match('/corrupt/i', $row->Msg_text)){ $wpdb->query("REPAIR TABLE " . $table_name); } } } } } // After optimization/repair, verify if the caller is a "ONCE" schedule, if so, change its settings in DB to inactive... if($schedule_params['repeat'] == "once"){ $schedule_params['active'] = "0"; $schedule_settings[$schedule_name] = $schedule_params; update_option('aDBc_optimize_schedule', $schedule_settings, "no"); } } } /** Returns an array containing all elements in general cleanup tab with their names used in the code */ function aDBc_return_array_all_elements_to_clean(){ $aDBc_unused["revision"]['name'] = __('Revisions','advanced-database-cleaner'); $aDBc_unused["revision"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-revisions-and-how-to-clean-them"; $aDBc_unused["auto-draft"]['name'] = __('Auto drafts','advanced-database-cleaner'); $aDBc_unused["auto-draft"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-auto-drafts-and-how-to-clean-them"; $aDBc_unused["trash-posts"]['name'] = __('Trashed posts','advanced-database-cleaner'); $aDBc_unused["trash-posts"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-trash-posts-and-how-to-clean-them"; $aDBc_unused["moderated-comments"]['name'] = __('Pending comments','advanced-database-cleaner'); $aDBc_unused["moderated-comments"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-pending-comments-and-how-to-clean-them"; $aDBc_unused["spam-comments"]['name'] = __('Spam comments','advanced-database-cleaner'); $aDBc_unused["spam-comments"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-spam-comments-and-how-to-clean-them"; $aDBc_unused["trash-comments"]['name'] = __('Trashed comments','advanced-database-cleaner'); $aDBc_unused["trash-comments"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-trash-comments-and-how-to-clean-them"; $aDBc_unused["pingbacks"]['name'] = __('Pingbacks','advanced-database-cleaner'); $aDBc_unused["pingbacks"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-pingbacks-and-how-to-clean-them"; $aDBc_unused["trackbacks"]['name'] = __('Trackbacks','advanced-database-cleaner'); $aDBc_unused["trackbacks"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-trackbacks-and-how-to-clean-them"; $aDBc_unused["orphan-postmeta"]['name'] = __('Orphaned post meta','advanced-database-cleaner'); $aDBc_unused["orphan-postmeta"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-orphan-posts-meta-and-how-to-clean-them"; $aDBc_unused["orphan-commentmeta"]['name'] = __('Orphaned comment meta','advanced-database-cleaner'); $aDBc_unused["orphan-commentmeta"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-orphan-comments-meta-and-how-to-clean-them"; $aDBc_unused["orphan-usermeta"]['name'] = __('Orphaned user meta','advanced-database-cleaner'); $aDBc_unused["orphan-usermeta"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-orphaned-user-meta-and-how-to-clean-them"; $aDBc_unused["orphan-termmeta"]['name'] = __('Orphaned term meta','advanced-database-cleaner'); $aDBc_unused["orphan-termmeta"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-orphaned-term-meta-and-how-to-clean-them"; $aDBc_unused["orphan-relationships"]['name'] = __('Orphaned relationships','advanced-database-cleaner'); $aDBc_unused["orphan-relationships"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-orphan-relationships-and-how-to-clean-them"; $aDBc_unused["expired-transients"]['name'] = __("Expired transients","advanced-database-cleaner"); $aDBc_unused["expired-transients"]['URL_blog'] = "https://sigmaplugin.com/blog/what-are-wordpress-transients"; return $aDBc_unused; } /** Counts all elements to clean (in the current site or MU) */ function aDBc_count_all_elements_to_clean(){ global $wpdb; $aDBc_unused = aDBc_return_array_all_elements_to_clean(); // Initialize counts to 0 foreach($aDBc_unused as $aDBc_type => $element_info){ $aDBc_unused[$aDBc_type]['count'] = 0; } //(for the table usermeta, only one table exists for MU, do not witch over blogs for it) if(function_exists('is_multisite') && is_multisite()){ $blogs_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach($blogs_ids as $blog_id){ switch_to_blog($blog_id); aDBc_count_elements_to_clean($aDBc_unused); restore_current_blog(); } }else{ aDBc_count_elements_to_clean($aDBc_unused); } return $aDBc_unused; } /** Counts elements to clean in the current site */ function aDBc_count_elements_to_clean(&$aDBc_unused){ global $wpdb; // Test if there are any keep_last options to count only elements with date < keep_lat to add it to the query $revision_date = aDBc_get_keep_last_sql_arg('revision','post_modified'); $auto_draft_date = aDBc_get_keep_last_sql_arg('auto-draft','post_modified'); $trash_post_date = aDBc_get_keep_last_sql_arg('trash-posts','post_modified'); $moderated_comment_date = aDBc_get_keep_last_sql_arg('moderated-comments','comment_date'); $spam_comment_date = aDBc_get_keep_last_sql_arg('spam-comments','comment_date'); $trash_comment_date = aDBc_get_keep_last_sql_arg('trash-comments','comment_date'); $pingback_date = aDBc_get_keep_last_sql_arg('pingbacks','comment_date'); $trackback_date = aDBc_get_keep_last_sql_arg('trackbacks','comment_date'); // Execute count queries $aDBc_unused["revision"]['count'] += $wpdb->get_var("SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = 'revision'" . $revision_date); $aDBc_unused["auto-draft"]['count'] += $wpdb->get_var("SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = 'auto-draft'" . $auto_draft_date); $aDBc_unused["trash-posts"]['count'] += $wpdb->get_var("SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = 'trash'" . $trash_post_date); $aDBc_unused["moderated-comments"]['count'] += $wpdb->get_var("SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = '0'" . $moderated_comment_date); $aDBc_unused["spam-comments"]['count'] += $wpdb->get_var("SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = 'spam'" . $spam_comment_date); $aDBc_unused["trash-comments"]['count'] += $wpdb->get_var("SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = 'trash'" . $trash_comment_date); $aDBc_unused["pingbacks"]['count'] += $wpdb->get_var("SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_type = 'pingback'" . $pingback_date); $aDBc_unused["trackbacks"]['count'] += $wpdb->get_var("SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_type = 'trackback'" . $trackback_date); $aDBc_unused["orphan-postmeta"]['count'] += $wpdb->get_var("SELECT COUNT(meta_id) FROM $wpdb->postmeta pm LEFT JOIN $wpdb->posts wp ON wp.ID = pm.post_id WHERE wp.ID IS NULL"); $aDBc_unused["orphan-commentmeta"]['count'] += $wpdb->get_var("SELECT COUNT(meta_id) FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_id FROM $wpdb->comments)"); // for the table usermeta, only one table exists for MU, do not switch over blogs for it. Get count only in main site if(is_main_site()){ $aDBc_unused["orphan-usermeta"]['count'] += $wpdb->get_var("SELECT COUNT(umeta_id) FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)"); } $aDBc_unused["orphan-termmeta"]['count'] += $wpdb->get_var("SELECT COUNT(meta_id) FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)"); $aDBc_unused["orphan-relationships"]['count'] += $wpdb->get_var("SELECT COUNT(object_id) FROM $wpdb->term_relationships WHERE term_taxonomy_id=1 AND object_id NOT IN (SELECT ID FROM $wpdb->posts)"); $expired_transient_names = $wpdb->get_col("SELECT REPLACE(option_name, '_timeout', '') FROM $wpdb->options where (option_name LIKE '_transient_timeout_%' OR option_name LIKE '_site_transient_timeout_%') AND option_value < UNIX_TIMESTAMP()"); $aDBc_unused["expired-transients"]['count'] += count($expired_transient_names); } /** Prepare keep_last element if any **/ function aDBc_get_keep_last_sql_arg($element_type, $column_name){ // If we are in MU, we should call settings from the main site since here in are inside switch_blog and therefore calling get_option will lead to calling the current blog options if(function_exists('is_multisite') && is_multisite()){ $settings = get_blog_option(ADBC_MAIN_SITE_ID, 'aDBc_settings'); }else{ $settings = get_option('aDBc_settings'); } if(!empty($settings['keep_last'])){ $keep_setting = $settings['keep_last']; if(!empty($keep_setting[$element_type])) return " and $column_name < NOW() - INTERVAL " . $keep_setting[$element_type] . " DAY"; } return ""; } /************************************************************************************************** * This function filters the array containing results according to users args for the free versions. * Mainly for tables to optimize and repair **************************************************************************************************/ function aDBc_filter_results_in_all_items_array_free(&$aDBc_all_items, $aDBc_tables_name_to_optimize, $aDBc_tables_name_to_repair){ if(function_exists('is_multisite') && is_multisite()){ // Filter according to tables types (to optimize, to repair...) if(!empty($_GET['t_type']) && $_GET['t_type'] != "all"){ $type = esc_sql($_GET['t_type']); if($type == 'optimize'){ $array_names = $aDBc_tables_name_to_optimize; }else{ $array_names = $aDBc_tables_name_to_repair; } foreach($aDBc_all_items as $item_name => $item_info){ foreach($item_info['sites'] as $site_id => $site_item_info){ if(!in_array($site_item_info['prefix'] . $item_name, $array_names)){ unset($aDBc_all_items[$item_name]['sites'][$site_id]); } } } } }else{ // Prepare an array containing names of items to delete $names_to_delete = array(); // Filter according to tables types (to optimize, to repair...) $filter_on_t_type = !empty($_GET['t_type']) && $_GET['t_type'] != "all"; if($filter_on_t_type){ $type = esc_sql($_GET['t_type']); if($type == "optimize"){ $array_names = $aDBc_tables_name_to_optimize; }else{ $array_names = $aDBc_tables_name_to_repair; } } foreach($aDBc_all_items as $item_name => $item_info){ if($filter_on_t_type){ if(!in_array($item_info['sites'][1]['prefix'] . $item_name, $array_names)){ array_push($names_to_delete, $item_name); } } } // Loop over the names to delete and delete them for the array foreach($names_to_delete as $name){ unset($aDBc_all_items[$name]); } } } /*********************************************************************************** * * Common function to: options, tables and scheduled tasks processes * ***********************************************************************************/ /** Prepares items (options, tables or tasks) to display + message*/ function aDBc_prepare_items_to_display( &$items_to_display, &$aDBc_items_categories_info, &$aDBc_which_button_to_show, $aDBc_tables_name_to_optimize, $aDBc_tables_name_to_repair, &$array_belongs_to_counts, &$aDBc_message, &$aDBc_class_message, $items_type){ // Prepare categories info switch($items_type){ case 'tasks' : $aDBc_all_items = aDBc_get_all_scheduled_tasks(); $aDBc_items_categories_info = array( 'all' => array('name' => __('All', 'advanced-database-cleaner'), 'color' => '#4E515B', 'count' => 0), 'u' => array('name' => __('Uncategorized', 'advanced-database-cleaner'), 'color' => 'grey', 'count' => 0), 'o' => array('name' => __('Orphans','advanced-database-cleaner'), 'color' => '#E97F31', 'count' => 0), 'p' => array('name' => __('Plugins tasks', 'advanced-database-cleaner'), 'color' => '#00BAFF', 'count' => 0), 't' => array('name' => __('Themes tasks', 'advanced-database-cleaner'), 'color' => '#45C966', 'count' => 0), 'w' => array('name' => __('WP tasks', 'advanced-database-cleaner'), 'color' => '#D091BE', 'count' => 0) ); break; case 'options' : $aDBc_all_items = aDBc_get_all_options(); $aDBc_items_categories_info = array( 'all' => array('name' => __('All', 'advanced-database-cleaner'), 'color' => '#4E515B', 'count' => 0), 'u' => array('name' => __('Uncategorized', 'advanced-database-cleaner'), 'color' => 'grey', 'count' => 0), 'o' => array('name' => __('Orphans','advanced-database-cleaner'), 'color' => '#E97F31', 'count' => 0), 'p' => array('name' => __('Plugins options', 'advanced-database-cleaner'), 'color' => '#00BAFF', 'count' => 0), 't' => array('name' => __('Themes options', 'advanced-database-cleaner'), 'color' => '#45C966', 'count' => 0), 'w' => array('name' => __('WP options', 'advanced-database-cleaner'), 'color' => '#D091BE', 'count' => 0) ); break; case 'tables' : $aDBc_all_items = aDBc_get_all_tables(); $aDBc_items_categories_info = array( 'all' => array('name' => __('All', 'advanced-database-cleaner'), 'color' => '#4E515B', 'count' => 0), 'u' => array('name' => __('Uncategorized', 'advanced-database-cleaner'), 'color' => 'grey', 'count' => 0), 'o' => array('name' => __('Orphans','advanced-database-cleaner'), 'color' => '#E97F31', 'count' => 0), 'p' => array('name' => __('Plugins tables', 'advanced-database-cleaner'), 'color' => '#00BAFF', 'count' => 0), 't' => array('name' => __('Themes tables', 'advanced-database-cleaner'), 'color' => '#45C966', 'count' => 0), 'w' => array('name' => __('WP tables', 'advanced-database-cleaner'), 'color' => '#D091BE', 'count' => 0) ); break; } if ( ADBC_PLUGIN_PLAN == "pro" ) { $aDBc_saved_items_file = ""; if(file_exists(ADBC_UPLOAD_DIR_PATH_TO_ADBC . "/" . $items_type . ".txt")){ $aDBc_saved_items_file = fopen(ADBC_UPLOAD_DIR_PATH_TO_ADBC . "/" . $items_type . ".txt", "r"); } $aDBc_manually_corrected_items_path = ADBC_UPLOAD_DIR_PATH_TO_ADBC . "/" . $items_type . "_corrected_manually.txt"; // Prepare an array containing user manually corrected results $aDBc_user_corrections = array(); if(file_exists($aDBc_manually_corrected_items_path)){ $aDBc_user_corrections = json_decode(trim(file_get_contents($aDBc_manually_corrected_items_path)), true); } // Affect type and belongs_to to items. if($aDBc_saved_items_file) { while(($item = fgets($aDBc_saved_items_file)) !== false) { $columns = explode(":", trim($item), 4); // We replace +=+ by : because names that contain : have been transformed to +=+ to prevent problems with split based on : $item_name = str_replace("+=+", ":", $columns[0]); // Prevent adding an item that was cleaned (maybe by other plugins) but not updated in file if(array_key_exists($item_name, $aDBc_all_items) && empty($aDBc_all_items[$item_name]['belongs_to'])) { // If needed, we correct items that users have corrected manually if(!empty($aDBc_user_corrections[$item_name])){ // If we are here, this means that the user has provided a correction to this item, we apply it $correction_by_user = $aDBc_user_corrections[$item_name]; $correction_by_user = explode(":", $correction_by_user); $aDBc_all_items[$item_name]['belongs_to'] = $correction_by_user[0]; $aDBc_all_items[$item_name]['type'] = $correction_by_user[1]; }else{ // By default, affect the plugin scan results to items $aDBc_all_items[$item_name]['belongs_to'] = $columns[1]; $aDBc_all_items[$item_name]['type'] = $columns[2]; // xxx verify if we should display info about orphaned items to which plugins/theme they may belong after double check // This information is stored in $columns[3] of each line if(!empty($columns[3])){ //$aDBc_all_items[$item_name]['corrections_info'] = aDBc_get_correction_info_for_orphaned_items($columns[3]); } } // Add this belongs_to to array for display in dropdown filter // Get only the first part in belongs_to with % $belongs_to_value = $aDBc_all_items[$item_name]['belongs_to']; $belongs_to_value = explode("(", $belongs_to_value, 2); $belongs_to_value = trim($belongs_to_value[0]); $belongs_to_value = str_replace(" ", "-", $belongs_to_value); // Get the type $belongs_to_type = $aDBc_all_items[$item_name]['type']; if($items_type == "tasks"){ if(!array_key_exists($belongs_to_value, $array_belongs_to_counts)){ $array_belongs_to_counts[$belongs_to_value]['type'] = $belongs_to_type; foreach($aDBc_all_items[$item_name]['sites'] as $site => $info){ $array_belongs_to_counts[$belongs_to_value]['count'] = count($aDBc_all_items[$item_name]['sites'][$site]['args']); } }else{ foreach($aDBc_all_items[$item_name]['sites'] as $site => $info){ $array_belongs_to_counts[$belongs_to_value]['count'] += count($aDBc_all_items[$item_name]['sites'][$site]['args']); } } }else{ if(!array_key_exists($belongs_to_value, $array_belongs_to_counts)){ $array_belongs_to_counts[$belongs_to_value]['type'] = $belongs_to_type; $array_belongs_to_counts[$belongs_to_value]['count'] = count($aDBc_all_items[$item_name]['sites']); }else{ $array_belongs_to_counts[$belongs_to_value]['count'] += count($aDBc_all_items[$item_name]['sites']); } } } } fclose($aDBc_saved_items_file); } } // Filter results according to users choices and args if ( ADBC_PLUGIN_PLAN == "pro" ) { aDBc_filter_results_in_all_items_array( $aDBc_all_items, $aDBc_tables_name_to_optimize, $aDBc_tables_name_to_repair ); } elseif ( ADBC_PLUGIN_PLAN == "free" ) { aDBc_filter_results_in_all_items_array_free( $aDBc_all_items, $aDBc_tables_name_to_optimize, $aDBc_tables_name_to_repair ); } // Put 'u' type to all uncategorized items and count all items foreach($aDBc_all_items as $item_name => $item_info){ // Counting items differ from tasks to options and tables // For tasks, we will counts numbers of args in array, while for options/tables, we will count number of sites if($items_type == "tasks"){ foreach($item_info['sites'] as $site => $info){ $aDBc_items_categories_info['all']['count'] += count($item_info['sites'][$site]['args']); if(empty($item_info['type'])){ $aDBc_all_items[$item_name]['type'] = 'u'; $aDBc_items_categories_info['u']['count'] += count($item_info['sites'][$site]['args']); }else{ $aDBc_items_categories_info[$item_info['type']]['count'] += count($item_info['sites'][$site]['args']); } } }else{ $aDBc_items_categories_info['all']['count'] += count($item_info['sites']); if(empty($item_info['type'])){ $aDBc_all_items[$item_name]['type'] = 'u'; $aDBc_items_categories_info['u']['count'] += count($item_info['sites']); }else{ $aDBc_items_categories_info[$item_info['type']]['count'] += count($item_info['sites']); } } } // Prepare items to display $aDBc_not_categorized_tooltip = ""; if ( ADBC_PLUGIN_PLAN == "pro" ) { $aDBc_not_categorized_tooltip = " " . __('This item is not categorized yet! Please click on scan button above to categorize it.','advanced-database-cleaner') ." "; } foreach ( $aDBc_all_items as $item_name => $item_info ) { if ( $_GET['aDBc_cat'] != "all" && $item_info['type'] != $_GET['aDBc_cat'] ) { continue; } switch ( $item_info['type'] ) { case 'u' : if ( ADBC_PLUGIN_PLAN == "pro" ) { $belongs_to_without_html = __( 'Uncategorized!', 'advanced-database-cleaner' ); } else { $belongs_to_without_html = __('Available in Pro version!', 'advanced-database-cleaner'); } $belongs_to = '' . $belongs_to_without_html . '' . $aDBc_not_categorized_tooltip; break; case 'o' : $belongs_to_without_html = __( 'Orphan!', 'advanced-database-cleaner' ); $belongs_to = '' . $belongs_to_without_html . ''; break; case 'w' : $belongs_to_without_html = __( 'Wordpress core', 'advanced-database-cleaner' ); $belongs_to = '' . $belongs_to_without_html; // Add percent % if any $belongs_to .= $item_info['belongs_to'] == "w" ? "" : " " . $item_info['belongs_to']; $belongs_to .= ''; break; case 'p' : $belongs_to_without_html = $item_info['belongs_to']; $belongs_to = '' . $belongs_to_without_html . ''; break; case 't' : $belongs_to_without_html = $item_info['belongs_to']; $belongs_to = '' . $belongs_to_without_html . ''; break; } foreach ( $item_info['sites'] as $site_id => $site_item_info ) { switch ( $items_type ) { case 'tasks' : foreach ( $site_item_info['args'] as $args_info ) { array_push( $items_to_display, array( 'hook_name' => $item_name, 'arguments' => $args_info['arguments'], 'site_id' => $site_id, 'next_run' => $args_info['next_run'] . ' - ' . $args_info['frequency'], 'timestamp' => $args_info['timestamp'], 'hook_belongs_to' => $belongs_to . $item_info['corrections_info'] ) ); } break; case 'options' : array_push( $items_to_display, array( 'option_name' => $item_name, 'option_value' => $site_item_info['value'], 'option_autoload' => $site_item_info['autoload'], 'option_size' => $site_item_info['size'], 'site_id' => $site_id, 'option_belongs_to' => $belongs_to . $item_info['corrections_info'] ) ); break; case 'tables' : array_push( $items_to_display, array( 'table_name' => $item_name, 'table_prefix' => $site_item_info['prefix'], 'table_full_name' => $site_item_info['prefix'].$item_name, 'table_rows' => $site_item_info['rows'], 'table_size' => $site_item_info['size'], 'table_lost' => $site_item_info['lost'], 'site_id' => $site_id, 'table_belongs_to' => $belongs_to . $item_info['corrections_info'] ) ); break; } } } // Sort items if necessary if(!empty($_GET['orderby'])){ $order_by = preg_replace('/[^a-zA-Z0-9_\.]/', '', $_GET['orderby']); $order = "asc"; if(!empty($_GET['order'])){ $order = 'DESC' === strtoupper( $_GET['order'] ) ? 'desc' : 'asc'; } if($order_by == "table_name"){ $order_by = "table_full_name"; } $elements = array(); foreach($items_to_display as $items){ $elements[] = $items[$order_by]; } if($order_by == "table_size" || $order_by == "option_size" || $order_by == "site_id"){ if($order == "asc"){ array_multisort($elements, SORT_ASC, $items_to_display, SORT_NUMERIC); }else{ array_multisort($elements, SORT_DESC, $items_to_display, SORT_NUMERIC); } }else{ if($order == "asc"){ array_multisort($elements, SORT_ASC, $items_to_display, SORT_REGULAR); }else{ array_multisort($elements, SORT_DESC, $items_to_display, SORT_REGULAR); } } } // Select which button to show, is it "new search" or "continue search"? // If $aDBc_saved_items['last_file_path'] contains a path, then we conclude that the last search has failed => display "continue searching" button $new_search = get_option("aDBc_temp_last_iteration_".$items_type); if(empty($new_search)){ $aDBc_which_button_to_show = "new_search"; }else{ $aDBc_which_button_to_show = "continue_search"; $aDBc_message .= ''; $aDBc_message .= __('This page will reload several times during this scan!', 'advanced-database-cleaner'); $aDBc_message .= ''; $aDBc_class_message = "notice-info"; } } /*********************************************************************************** * * Function proper to options processes * ***********************************************************************************/ /** Prepares all options for all sites (if any) in a multidimensional array */ function aDBc_get_all_options() { $aDBc_all_options = array(); global $wpdb; if(function_exists('is_multisite') && is_multisite()){ $blogs_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach($blogs_ids as $blog_id){ switch_to_blog($blog_id); aDBc_add_options($aDBc_all_options, $blog_id); restore_current_blog(); } }else{ aDBc_add_options($aDBc_all_options, "1"); } return $aDBc_all_options; } /** Prepares options for one single site (Used by aDBc_get_all_options() function) */ function aDBc_add_options(&$aDBc_all_options, $blog_id){ global $wpdb; // Get the list of all options from the current WP database $aDBc_options_in_db = $wpdb->get_results("SELECT option_name, option_value, autoload FROM $wpdb->options WHERE option_name NOT LIKE '%transient%' and option_name NOT LIKE '%session%expire%'"); foreach($aDBc_options_in_db as $option){ // If the option has not been added yet, add it and initiate its info if(empty($aDBc_all_options[$option->option_name])){ $aDBc_all_options[$option->option_name] = array('belongs_to' => '', 'maybe_belongs_to' => '', 'corrections_info' => '', 'type' => '', 'sites' => array()); } // Add info of the option according to the current site $aDBc_all_options[$option->option_name]['sites'][$blog_id] = array( 'value' => aDBc_create_tooltip_for_option_value($option->option_value, 17), 'size' => mb_strlen($option->option_value), 'autoload' => $option->autoload ); } } /*********************************************************************************** * * Function proper to tables processes * ***********************************************************************************/ /** Prepares all tables for all sites (if any) in a multidimensional array */ function aDBc_get_all_tables() { global $wpdb; // First, prepare an array containing rows and sizes of tables $aDBc_tables_rows_sizes = array(); $aDBc_result = $wpdb->get_results('SHOW TABLE STATUS FROM `'.DB_NAME.'`'); foreach($aDBc_result as $aDBc_row){ $aDBc_table_size = $aDBc_row->Data_length + $aDBc_row->Index_length; $aDBc_table_lost = $aDBc_row->Data_free; $aDBc_tables_rows_sizes[$aDBc_row->Name] = array('rows' => $aDBc_row->Rows, 'size' => $aDBc_table_size, 'lost' => $aDBc_table_lost); } // Prepare ana array to hold all info about tables $aDBc_all_tables = array(); $aDBc_prefix_list = array(); // If is Multisite then we retrieve the list of all prefixes if(function_exists('is_multisite') && is_multisite()){ $blogs_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach($blogs_ids as $blog_id){ $aDBc_prefix_list[$wpdb->get_blog_prefix($blog_id)] = $blog_id; } }else{ $aDBc_prefix_list[$wpdb->prefix] = "1"; } // Get the names of all tables in the database $aDBc_all_tables_names = $wpdb->get_results("SELECT table_name FROM information_schema.tables WHERE table_schema = '" . DB_NAME . "'"); foreach($aDBc_all_tables_names as $aDBc_table){ // Get table name $table_name = ""; // This test to prevent issues in MySQL 8 where tables are not shown // MySQL 5 uses $aDBc_table->table_name while MySQL 8 uses $aDBc_table->TABLE_NAME if(property_exists($aDBc_table, "table_name")){ $table_name = $aDBc_table->table_name; }else if(property_exists($aDBc_table, "TABLE_NAME")){ $table_name = $aDBc_table->TABLE_NAME; } // Holds the possible prefixes found for the current table $aDBc_found_prefixes = array(); // Test if the table name starts with a valid prefix foreach($aDBc_prefix_list as $prefix => $site_id){ if(substr($table_name, 0, strlen($prefix)) === $prefix){ $aDBc_found_prefixes[$prefix] = $site_id; } } // If the table do not start with any valid prefix, we add it as it is if(count($aDBc_found_prefixes) == 0){ $aDBc_table_name_without_prefix = $table_name; $aDBc_table_prefix = ""; $aDBc_table_site = "1"; }else if(count($aDBc_found_prefixes) == 1){ // If the number of possible prefixes found is 1, we add the table name with its data // Get the first element in $aDBc_found_prefixes reset($aDBc_found_prefixes); $aDBc_table_prefix = key($aDBc_found_prefixes); $aDBc_table_site = current($aDBc_found_prefixes); $aDBc_table_name_without_prefix = substr($table_name, strlen($aDBc_table_prefix)); }else{ // If the number of possible prefixes found >= 2, we choose the longest prefix as valid one $aDBc_table_prefix = ""; $aDBc_table_site = ""; $aDBc_table_name_without_prefix = ""; foreach($aDBc_found_prefixes as $aDBc_prefix => $aDBc_site){ if(strlen($aDBc_prefix) >= strlen($aDBc_table_prefix)){ $aDBc_table_prefix = $aDBc_prefix; $aDBc_table_site = $aDBc_site; $aDBc_table_name_without_prefix = substr($table_name, strlen($aDBc_table_prefix)); } } } // Add table information to the global array // If the table has not been added yet, add it and initiate its info if(empty($aDBc_all_tables[$aDBc_table_name_without_prefix])){ $aDBc_all_tables[$aDBc_table_name_without_prefix] = array('belongs_to' => '', 'maybe_belongs_to' => '', 'corrections_info' => '', 'type' => '', 'sites' => array()); } // Add info of the task according to the current site $aDBc_all_tables[$aDBc_table_name_without_prefix]['sites'][$aDBc_table_site] = array('prefix' => $aDBc_table_prefix, 'rows' => $aDBc_tables_rows_sizes[$table_name]['rows'], 'size' => $aDBc_tables_rows_sizes[$table_name]['size'], 'lost' => $aDBc_tables_rows_sizes[$table_name]['lost'], ); } return $aDBc_all_tables; } /*********************************************************************************** * * Function proper to scheduled tasks processes * ***********************************************************************************/ /** Prepares all scheduled tasks for all sites (if any) in a multidimensional array */ function aDBc_get_all_scheduled_tasks() { $aDBc_all_tasks = array(); if(function_exists('is_multisite') && is_multisite()){ global $wpdb; $blogs_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); foreach($blogs_ids as $blog_id){ switch_to_blog($blog_id); aDBc_add_scheduled_tasks($aDBc_all_tasks, $blog_id); restore_current_blog(); } }else{ aDBc_add_scheduled_tasks($aDBc_all_tasks, "1"); } return $aDBc_all_tasks; } /** Prepares scheduled tasks for one single site (Used by aDBc_get_all_scheduled_tasks() function) */ function aDBc_add_scheduled_tasks(&$aDBc_all_tasks, $blog_id) { $cron = _get_cron_array(); $schedules = wp_get_schedules(); foreach((array) $cron as $timestamp => $cronhooks){ foreach( (array) $cronhooks as $hook => $events){ foreach( (array) $events as $event){ // If the frequency exist if($event['schedule']){ if(!empty($schedules[$event['schedule']])){ $aDBc_frequency = $schedules[$event['schedule']]['display']; }else{ $aDBc_frequency = __('Unknown!', 'advanced-database-cleaner'); } }else{ $aDBc_frequency = __('Single event', 'advanced-database-cleaner'); } // Get arguments $aDBc_args_array = array(); if(!empty($event['args'])){ $aDBc_args = $event['args']; foreach( (array) $aDBc_args as $id => $arg){ array_push($aDBc_args_array, $arg); } } if(empty($aDBc_args_array)){ $args_string = "none"; }else{ $args_string = json_encode($aDBc_args_array); } // If the task has not been added yet, add it and initiate its info if(empty($aDBc_all_tasks[$hook])){ $aDBc_all_tasks[$hook] = array('belongs_to' => '', 'maybe_belongs_to' => '', 'corrections_info' => '', 'type' => '', 'sites' => array()); } // Initialize args array if(empty($aDBc_all_tasks[$hook]['sites'][$blog_id]['args'])){ $aDBc_all_tasks[$hook]['sites'][$blog_id]['args'] = array(); } array_push($aDBc_all_tasks[$hook]['sites'][$blog_id]['args'], array('frequency' => $aDBc_frequency, 'next_run' => get_date_from_gmt(date('Y-m-d H:i:s', (int) $timestamp), 'M j, Y @ H:i:s'), 'timestamp' => $timestamp, 'arguments' => $args_string)); } } } } /*********************************************************************************** * Transform bytes to corresponding best size system: KB, MB or GB ***********************************************************************************/ function aDBc_get_size_from_bytes($bytes) { $size = $bytes / 1024; if($size >= 1024){ $size = $size / 1024; if($size >= 1024){ $size = $size / 1024; $size = round($size, 1) . " GB"; }else{ $size = round($size, 1) . " MB"; } }else{ $size = round($size, 1) . " KB"; } return $size; } /*********************************************************************************** * Create the folder plus an index.php for silence is golden ***********************************************************************************/ function aDBc_create_folder_plus_index_file($folder) { wp_mkdir_p($folder); // Create index file $myfile = fopen($folder . '/index.php', "w"); if($myfile){ fwrite($myfile, "get_col("SELECT blog_id FROM $wpdb->blogs"); foreach($blogs_ids as $blog_id){ array_push($aDBc_wp_core_options, $wpdb->get_blog_prefix($blog_id).'user_roles'); } } return $aDBc_wp_core_options; } /*********************************************************************************** * Get All options and tasks used by the ADBC plugin ***********************************************************************************/ function aDBc_get_ADBC_options_and_tasks_names() { // yyy: Always make sure to keep this list up to date and put here only valid options after scan not temp ones that will be deleted $aDBc_names = array( // Active options 'aDBc_settings', 'aDBc_security_folder_code', 'aDBc_edd_license_key', 'aDBc_edd_license_status', // Scheduled tasks 'aDBc_optimize_schedule', 'aDBc_clean_schedule', 'aDBc_last_search_ok_tables', 'aDBc_last_search_ok_options', 'aDBc_last_search_ok_tasks' ); return $aDBc_names; } /*********************************************************************************** * Save settings ***********************************************************************************/ function aDBc_save_settings_callback() { check_ajax_referer( 'aDBc_nonce', 'security' ); if ( ! current_user_can( 'administrator' ) ) wp_send_json_error( __( 'Not sufficient permissions!', 'advanced-database-cleaner' ) ); $aDBc_settings = get_option( 'aDBc_settings' ); // Data validation $left_menu = intval( $_REQUEST['left_menu'] ); $menu_under_tools = intval( $_REQUEST['menu_under_tools'] ); $hide_premium_tab = intval( $_REQUEST['hide_premium_tab'] ); $allowed_values = array(0, 1); if ( ! in_array( $left_menu, $allowed_values, true ) || ! in_array( $menu_under_tools, $allowed_values, true ) || ! in_array( $hide_premium_tab, $allowed_values, true ) ) { wp_send_json_error( __( 'An error has occurred. Please try again!', 'advanced-database-cleaner' ) ); } if ( $left_menu == 0 && $menu_under_tools == 0 ) wp_send_json_error( __( 'Please select at least one menu to show the plugin', 'advanced-database-cleaner' ) ); // Set new values $aDBc_settings['left_menu'] = $left_menu; $aDBc_settings['menu_under_tools'] = $menu_under_tools; if ( ADBC_PLUGIN_PLAN == "free" ) $aDBc_settings['hide_premium_tab'] = $hide_premium_tab; update_option( 'aDBc_settings', $aDBc_settings, "no" ); // If no error reported before, success and die wp_send_json_success(); } ?>