version = $this->get_plugin_version();
$this->plugin_dir = plugin_dir_path(__FILE__);
$this->plugin_url = plugin_dir_url(__FILE__);
$this->load_options();
add_action('admin_menu', array($this, 'admin_menu'));
add_action('admin_init', array($this, 'do_all_actions'));
add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
add_action('wp_ajax_wp_reset_dismiss_notice', array($this, 'ajax_dismiss_notice'));
add_action('wp_ajax_wp_reset_run_tool', array($this, 'ajax_run_tool'));
add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'));
add_filter('plugin_row_meta', array($this, 'plugin_meta_links'), 10, 2);
add_filter('admin_footer_text', array($this, 'admin_footer_text'));
$this->core_tables = array_map(function($tbl) { global $wpdb; return $wpdb->prefix . $tbl; }, $this->core_tables);
} // __construct
/**
* Get plugin version from file header
*
* @return string
*/
function get_plugin_version() {
$plugin_data = get_file_data(__FILE__, array('version' => 'Version'), 'plugin');
return $plugin_data['version'];
} // get_plugin_version
/**
* Load and prepare the options array
* If needed create a new DB entry
*
* @return array
*/
private function load_options() {
$options = get_option('wp-reset', array());
$change = false;
if (!isset($options['meta'])) {
$options['meta'] = array('first_version' => $this->version, 'first_install' => current_time('timestamp', true), 'reset_count' => 0);
$change = true;
}
if (!isset($options['dismissed_notices'])) {
$options['dismissed_notices'] = array();
$change = true;
}
if (!isset($options['last_run'])) {
$options['last_run'] = array();
$change = true;
}
if (!isset($options['options'])) {
$options['options'] = array();
$change = true;
}
if ($change) {
update_option('wp-reset', $options, true);
}
$this->options = $options;
return $options;
} // load_options
/**
* Get meta part of plugin options
*
* @return array
*/
function get_meta() {
return $this->options['meta'];
} // get_meta
/**
* Get all dismissed notices, or check for one specific notice
*
* @param string $notice_name Optional. Check if specified notice is dismissed.
*
* @return bool|array
*/
function get_dismissed_notices($notice_name = '') {
$notices = $this->options['dismissed_notices'];
if (empty($notice_name)) {
return $notices;
} else {
if (empty($notices[$notice_name])) {
return false;
} else {
return true;
}
}
} // get_dismissed_notices
/**
* Get options part of plugin options
*
* todo: not completed
*
* @param string $key Optional.
*
* @return array
*/
function get_options($key = '') {
return $this->options['options'];
} // get_options
/**
* Update plugin options, currently entire array
*
* todo: this handles the entire options array although it should only do the options part - it's confusing
*
* @param string $key Data to save.
* @param string $data Option key.
*
* @return bool
*/
function update_options($key, $data) {
$this->options[$key] = $data;
$tmp = update_option('wp-reset', $this->options);
return $tmp;
} // set_options
/**
* Add plugin menu entry under Tools menu
*
* @return null
*/
function admin_menu() {
add_management_page(__('WP Reset', 'wp-reset'), __('WP Reset', 'wp-reset'), 'administrator', 'wp-reset', array($this, 'plugin_page'));
} // admin_menu
/**
* Dismiss notice via AJAX call
*
* @return null
*/
function ajax_dismiss_notice() {
check_ajax_referer('wp-reset_dismiss_notice');
$notice_name = trim(@$_GET['notice_name']);
if (!$this->dismiss_notice($notice_name)) {
wp_send_json_error('Notice is already dismissed.');
} else {
wp_send_json_success();
}
} // ajax_dismiss_notice
/**
* Dismiss notice by adding it to dismissed_notices options array
*
* @param string $notice_name Notice to dismiss.
*
* @return bool
*/
function dismiss_notice($notice_name) {
if ($this->get_dismissed_notices($notice_name)) {
return false;
} else {
$notices = $this->get_dismissed_notices();
$notices[$notice_name] = true;
$this->update_options('dismissed_notices', $notices);
return true;
}
} // dismiss_notice
/**
* Returns all WP pointers
*
* @return array
*/
function get_pointers() {
$pointers = array();
$pointers['welcome'] = array('target' => '#menu-tools', 'edge' => 'left', 'align' => 'right', 'content' => 'Thank you for installing the WP Reset plugin!
Open Tools - WP Reset to access resetting tools and start developing & debugging faster.');
return $pointers;
} // get_pointers
/**
* Enqueue CSS and JS files
*
* @return null
*/
function admin_enqueue_scripts($hook) {
// welcome pointer is shown on all pages except WPR, untill dismissed
$pointers = $this->get_pointers();
$dismissed_notices = $this->get_dismissed_notices();
foreach ($dismissed_notices as $notice_name => $tmp) {
if ($tmp) {
unset($pointers[$notice_name]);
}
} // foreach
if (!empty($pointers) && 'tools_page_wp-reset' != $hook) {
$pointers['_nonce_dismiss_pointer'] = wp_create_nonce('wp-reset_dismiss_notice');
wp_enqueue_style('wp-pointer');
wp_enqueue_script('wp-reset-pointers', $this->plugin_url . 'js/wp-reset-pointers.js', array('jquery'), $this->version, true);
wp_enqueue_script('wp-pointer');
wp_localize_script('wp-pointer', 'wp_reset_pointers', $pointers);
}
// exit early if not on WP Reset page
if ('tools_page_wp-reset' != $hook) {
return;
}
$options = $this->get_options();
$js_localize = array('undocumented_error' => __('An undocumented error has occured. Please refresh the page and try again.', 'wp-reset'),
'documented_error' => __('An error has occured.', 'wp-reset'),
'plugin_name' => __('WP Reset', 'wp-reset'),
'settings_url' => admin_url('tools.php?page=wp-reset'),
'icon_url' => $this->plugin_url . 'img/wp-reset-icon.png',
'invalid_confirmation' => __('Please type "reset" in the confirmation field.', 'wp-reset'),
'invalid_confirmation_title' => __('Invalid confirmation', 'wp-reset'),
'cancel_button' => __('Cancel', 'wp-reset'),
'ok_button' => __('OK', 'wp-reset'),
'confirm_button' => __('Reset WordPress', 'wp-reset'),
'confirm_title' => __('Are you sure you want to proceed?', 'wp-reset'),
'confirm1' => __('Clicking "Reset WordPress" will reset your site to default values. All content will be lost. There is NO UNDO.', 'wp-reset'),
'confirm2' => __('Click "Cancel" to abort.', 'wp-reset'),
'doing_reset' => __('Resetting in progress. Please wait.', 'wp-reset'),
'nonce_dismiss_notice' => wp_create_nonce('wp-reset_dismiss_notice'),
'nonce_run_tool' => wp_create_nonce('wp-reset_run_tool'),
'nonce_do_reset' => wp_create_nonce('wp-reset_do_reset'));
wp_enqueue_style('wp-reset', $this->plugin_url . 'css/wp-reset.css', array(), $this->version);
wp_enqueue_style('wp-reset-sweetalert2', $this->plugin_url . 'css/sweetalert2.min.css', array(), $this->version);
wp_enqueue_script('jquery-ui-tabs');
wp_enqueue_script('wp-reset-sweetalert2', $this->plugin_url . 'js/sweetalert2.min.js', array('jquery'), $this->version, true);
wp_enqueue_script('wp-reset', $this->plugin_url . 'js/wp-reset.js', array('jquery'), $this->version, true);
wp_localize_script('wp-reset', 'wp_reset', $js_localize);
// fix for aggressive plugins that include their CSS on all pages
wp_dequeue_style('uiStyleSheet');
wp_dequeue_style('wpcufpnAdmin' );
wp_dequeue_style('unifStyleSheet' );
wp_dequeue_style('wpcufpn_codemirror');
wp_dequeue_style('wpcufpn_codemirrorTheme');
wp_dequeue_style('collapse-admin-css');
wp_dequeue_style('jquery-ui-css');
wp_dequeue_style('tribe-common-admin');
wp_dequeue_style('file-manager__jquery-ui-css');
wp_dequeue_style('file-manager__jquery-ui-css-theme');
wp_dequeue_style('wpmegmaps-jqueryui');
wp_dequeue_style('wp-botwatch-css');
} // admin_enqueue_scripts
/**
* Check if WP-CLI is available and running
*
* @return bool
*/
function is_cli_running() {
if (defined('WP_CLI') && WP_CLI) {
return true;
} else {
return false;
}
} // is_cli_running
/**
* Deletes all transients.
*
* @return int Number of deleted transient DB entries
*/
function do_delete_transients() {
global $wpdb;
$count = $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '\_transient\_%' OR option_name LIKE '\_site\_transient\_%'");
return $count;
} // do_delete_transients
/**
* Deletes all files in uploads folder.
*
* @return int Number of deleted files and folders.
*/
function do_delete_uploads() {
$upload_dir = wp_get_upload_dir();
$this->delete_folder($upload_dir['basedir'], $upload_dir['basedir']);
return $this->delete_count;
} // do_delete_uploads
/**
* Recursively deletes a folder
*
* @param string $folder Recursive param.
* @param string $base_folder Base folder.
*
* @return bool
*/
private function delete_folder($folder, $base_folder) {
$files = array_diff(scandir($folder), array('.', '..'));
foreach ($files as $file) {
if (is_dir($folder . DIRECTORY_SEPARATOR . $file)) {
$this->delete_folder($folder . DIRECTORY_SEPARATOR . $file, $base_folder);
} else {
$tmp = @unlink($folder . DIRECTORY_SEPARATOR . $file);
$this->delete_count = $this->delete_count + (int) $tmp;
}
} // foreach
if ($folder != $base_folder) {
$tmp = @rmdir($folder);
$this->delete_count = $this->delete_count + (int) $tmp;
return $tmp;
} else {
return true;
}
} // delete_folder
/**
* Deactivate and delete all plugins
*
* @param bool $keep_wp_reset Keep WP Reset active and installed
* @param bool $silent_deactivate Skip individual plugin deactivation functions when deactivating
*
* @return int Number of deleted plugins.
*/
function do_delete_plugins($keep_wp_reset = true, $silent_deactivate = false) {
if (!function_exists('get_plugins')) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$wp_reset_basename = plugin_basename(__FILE__);
$all_plugins = get_plugins();
$active_plugins = (array) get_option('active_plugins', array());
if (true == $keep_wp_reset) {
if (($key = array_search($wp_reset_basename, $active_plugins)) !== false) {
unset($active_plugins[$key]);
}
unset($all_plugins[$wp_reset_basename]);
}
if (!empty($active_plugins)) {
deactivate_plugins($active_plugins, $silent_deactivate, false);
}
if (!empty($all_plugins)) {
delete_plugins(array_keys($all_plugins));
}
return sizeof($all_plugins);
} // do_delete_plugins
/**
* Delete all themes
*
* @param bool $keep_default_theme Keep default theme
*
* @return int Number of deleted themes.
*/
function do_delete_themes($keep_default_theme = true) {
$default_theme = 'twentyseventeen';
$all_themes = wp_get_themes(array('errors' => null));
if (true == $keep_default_theme) {
unset($all_themes[$default_theme]);
}
foreach ($all_themes as $theme_slug => $theme_details) {
$res = delete_theme($theme_slug);
}
if (false == $keep_default_theme) {
update_option('template', '');
update_option('stylesheet', '');
}
return sizeof($all_themes);
} // do_delete_themes
/**
* Run one tool via AJAX call
*
* @return null
*/
function ajax_run_tool() {
check_ajax_referer('wp-reset_run_tool');
$tool = trim(@$_GET['tool']);
$extra_data = trim(@$_GET['extra_data']);
if ($tool == 'delete_transients') {
$cnt = $this->do_delete_transients();
wp_send_json_success($cnt);
} elseif ($tool == 'delete_themes') {
$cnt = $this->do_delete_themes(false);
wp_send_json_success($cnt);
} elseif ($tool == 'delete_plugins') {
$cnt = $this->do_delete_plugins(true);
wp_send_json_success($cnt);
} elseif ($tool == 'delete_uploads') {
$cnt = $this->do_delete_uploads();
wp_send_json_success($cnt);
} elseif ($tool == 'delete_snapshot') {
$res = $this->do_delete_snapshot($extra_data);
if (is_wp_error($res)) {
wp_send_json_error($res->get_error_message());
} else {
wp_send_json_success();
}
} elseif ($tool == 'download_snapshot') {
$res = $this->do_export_snapshot($extra_data);
if (is_wp_error($res)) {
wp_send_json_error($res->get_error_message());
} else {
$url = content_url() . '/' . $this->snapshots_folder . '/' . $res;
wp_send_json_success($url);
}
} elseif ($tool == 'restore_snapshot') {
$res = $this->do_restore_snapshot($extra_data);
if (is_wp_error($res)) {
wp_send_json_error($res->get_error_message());
} else {
wp_send_json_success();
}
} elseif ($tool == 'compare_snapshots') {
$res = $this->do_compare_snapshots($extra_data);
if (is_wp_error($res)) {
wp_send_json_error($res->get_error_message());
} else {
wp_send_json_success($res);
}
} elseif ($tool == 'create_snapshot') {
$res = $this->do_create_snapshot($extra_data);
if (is_wp_error($res)) {
wp_send_json_error($res->get_error_message());
} else {
wp_send_json_success();
}
} else {
wp_send_json_error(__('Unknown tool.', 'wp-reset'));
}
} // ajax_run_tool
/**
* Reinstall / reset the WP site
* There are no failsafes in the function - it reinstalls when called
* Redirects when done
*
* @param array $params Optional.
*
* @return null
*/
function do_reinstall($params = array()) {
global $current_user, $wpdb;
// only admins can reset; double-check
if (!$this->is_cli_running() && !current_user_can('administrator')) {
return false;
}
// make sure the function is available to us
if (!function_exists('wp_install')) {
require ABSPATH . '/wp-admin/includes/upgrade.php';
}
// save values that need to be restored after reset
// todo: use params to determine what gets restored after reset
$blogname = get_option('blogname');
$blog_public = get_option('blog_public');
$wplang = get_option('wplang');
$siteurl = get_option('siteurl');
$home = get_option('home');
$snapshots = $this->get_snapshots();
$active_plugins = get_option('active_plugins');
$active_theme = wp_get_theme();
// for WP-CLI
if (!$current_user->ID) {
$tmp = get_users(array('role' => 'administrator', 'order' => 'ASC', 'order_by' => 'ID'));
if (empty($tmp[0]->user_login)) {
return new WP_Error('no_user', 'Reset failed. Unable to find any admin users in database.');
}
$current_user = $tmp[0];
}
// delete custom tables with WP's prefix
$prefix = str_replace('_', '\_', $wpdb->prefix);
$tables = $wpdb->get_col("SHOW TABLES LIKE '{$prefix}%'");
foreach ($tables as $table) {
$wpdb->query("DROP TABLE $table");
}
// supress errors for WP_CLI
// todo: do something better
$result = @wp_install($blogname, $current_user->user_login, $current_user->user_email, $blog_public, '', md5(rand()), $wplang);
$user_id = $result['user_id'];
// restore user pass
$query = $wpdb->prepare("UPDATE {$wpdb->users} SET user_pass = %s, user_activation_key = '' WHERE ID = %d LIMIT 1", array($current_user->user_pass, $user_id));
$wpdb->query($query);
// restore rest of the settings including WP Reset's
update_option('siteurl', $siteurl);
update_option('home', $home);
update_option('wp-reset', $this->options);
update_option('wp-reset-snapshots', $snapshots);
// remove password nag
if (get_user_meta($user_id, 'default_password_nag')) {
update_user_meta($user_id, 'default_password_nag', false);
}
if (get_user_meta($user_id, $wpdb->prefix . 'default_password_nag')) {
update_user_meta($user_id, $wpdb->prefix . 'default_password_nag', false );
}
$meta = $this->get_meta();
$meta['reset_count']++;
$this->update_options('meta', $meta);
// reactivate theme
if (!empty($params['reactivate_theme'])) {
switch_theme($active_theme->get_stylesheet());
}
// reactivate WP Reset
if (!empty($params['reactivate_wpreset'])) {
activate_plugin(plugin_basename( __FILE__ ));
}
// reactivate all plugins
if (!empty($params['reactivate_plugins'])) {
foreach ($active_plugins as $plugin_file) {
activate_plugin($plugin_file);
}
}
if (!$this->is_cli_running()) {
// log out and log in the old/new user
// since the password doesn't change this is potentially unnecessary
wp_clear_auth_cookie();
wp_set_auth_cookie($user_id);
wp_redirect(admin_url() . '?wp-reset=success');
exit;
}
} // do_reinstall
/**
* Checks wp_reset post value and performs all actions
* todo: handle messages for various actions
*
* @return null|bool
*/
function do_all_actions() {
// only admins can perform actions
if (!current_user_can('administrator')) {
return;
}
if (!empty($_GET['wp-reset']) && stristr($_SERVER['HTTP_REFERER'], 'wp-reset')) {
add_action('admin_notices', array($this, 'notice_successfull_reset'));
}
// check nonce
if (true === isset($_POST['wp_reset_confirm']) && false === wp_verify_nonce(@$_POST['_wpnonce'], 'wp-reset')) {
add_settings_error('wp-reset', 'bad-nonce', __('Something went wrong. Please refresh the page and try again.', 'wp-reset'), 'error');
return false;
}
// check confirmation code
if (true === isset($_POST['wp_reset_confirm']) && 'reset' !== $_POST['wp_reset_confirm']) {
add_settings_error('wp-reset', 'bad-confirm', __('Invalid confirmation code. Please type "reset" in the confirmation field.', 'wp-reset'), 'error');
return false;
}
// only one action at the moment
if (true === isset($_POST['wp_reset_confirm']) && 'reset' === $_POST['wp_reset_confirm']) {
$defaults = array('reactivate_theme' => '0',
'reactivate_plugins' => '0',
'reactivate_wpreset' => '0');
$params = shortcode_atts($defaults, (array) @$_POST['wpr-post-reset']);
$this->do_reinstall($params);
}
} // do_all_actions
/**
* Add "Reset WordPress" action link to plugins table, left part
*
* @param array $links Initial list of links.
*
* @return array
*/
function plugin_action_links($links) {
$settings_link = '' . __('Reset WordPress', 'wp-reset') . '';
array_unshift($links, $settings_link);
return $links;
} // plugin_action_links
/**
* Add links to plugin's description in plugins table
*
* @param array $links Initial list of links.
* @param string $file Basename of current plugin.
*
* @return array
*/
function plugin_meta_links($links, $file) {
if ($file !== plugin_basename(__FILE__)) {
return $links;
}
$support_link = '' . __('Support', 'wp-reset') . '';
$home_link = '' . __('Plugin Homepage', 'wp-reset') . '';
$rate_link = '' . __('Rate the plugin ★★★★★', 'wp-reset') . '';
$links[] = $support_link;
$links[] = $home_link;
$links[] = $rate_link;
return $links;
} // plugin_meta_links
/**
* Test if we're on WPR's admin page
*
* @return bool
*/
function is_plugin_page() {
$current_screen = get_current_screen();
if ($current_screen->id == 'tools_page_wp-reset') {
return true;
} else {
return false;
}
} // is_plugin_page
/**
* Add powered by text in admin footer
*
* @param string $text Default footer text.
*
* @return string
*/
function admin_footer_text($text) {
if (!$this->is_plugin_page()) {
return $text;
}
$text = 'WP Reset v' . $this->version . ' by WebFactory Ltd. Proudly sponsored by IP Geolocation - premium GeoIP service for developers.';
return $text;
} // admin_footer_text
/**
* Loads plugin's translated strings
*
* @return null
*/
function load_textdomain() {
load_plugin_textdomain('wp-reset');
} // load_textdomain
/**
* Inform the user that WordPress has been successfully reset
*
* @return null
*/
function notice_successfull_reset() {
global $current_user;
echo '
' . sprintf(__( 'Site has been reset to default settings. User "%s" was restored with the password unchanged. Open WP Reset to do another reset.', 'wp-reset'), $current_user->user_login, admin_url('tools.php?page=wp-reset')) . '
proudly sponsored by IP Geolocation' . sprintf(__('All features available via GUI are available in WP-CLI as well. To get the list of commands run %s. Instead of the active user, the first user with admin privileges found in the database will be restored. ', 'wp-reset'), 'wp help reset');
echo sprintf(__('All actions have to be confirmed. If you want to skip confirmation use the standard %s option. Please be carefull - there is NO UNDO.', 'wp-reset'), '--yes') . '
' . __('Type reset in the confirmation field to confirm the reset and then click the "Reset WordPress" button. There is NO UNDO. No backups are made by WP Reset.', 'wp-reset') . '
'; wp_nonce_field('wp-reset'); echo ''; echo '
'; echo '' . __('All transient related database entries will be deleted. Including expired and non-expired transients, and orphaned timeout entries. There is NO UNDO. WP Reset will not make any backups.', 'wp-reset') . '
'; echo ''; echo '' . __('All files in ' . $upload_dir['basedir'] . ' folder will be deleted. Including folders and subfolder, and files in subfolders. Files associated with media entries will be deleted too. There is NO UNDO. WP Reset will not make any backups.', 'wp-reset') . '
Tool is not available. Folder is not writeable by WordPress. Please check file and folder access rights.
'; } else { echo ''; } echo '' . __('All themes will be deleted. Including the currently active theme - ' . $theme->get('Name') . '. There is NO UNDO. WP Reset will not make any backups.', 'wp-reset') . '
'; echo ''; echo '' . __('Type reset in the confirmation field to confirm the reset and then click the "Reset WordPress" button. There is NO UNDO. WP Reset will not make any backups.', 'wp-reset') . '
'; echo 'WP Reset plugin will no be deleted or disabled.
'; echo ''; echo '' . __('We are very active on the official WP Reset support forum. If you found a bug, have a feature idea or just want to say hi - please drop by. We love to hear back from our users.', 'wp-reset') . '
'; echo '' . __('If there\'s a need to contact us privately send emails to wpreset@webfactoryltd.com. Please know that although we\'ll gladly have a look at issues you are having with any site, we can\'t promise we\'ll fix them. Thank you for understanding.', 'wp-reset') . '
'; echo '' . __('No need for donations or anything like that :) If you can give us a five star rating you\'ll help out more than you can imagine. Thank you!', 'wp-reset') . '
'; echo 'A snapshot is a copy of all WP database tables, standard and custom ones, saved in your database. Files are not saved or included in snapshots in any way.
Snapshots are primarily a development tool. Although they can be used for backups (and downloaded), we suggest finding a more suitable tool for live sites, such as UpdraftPlus. Use snapshots to find out what changes a plugin made to your database or to quickly restore the dev environment after testing database related changes.
Restoring a snapshot does not affect other snapshots, or WP Reset settings.
Snapshots are still in development. If you see a bug or just have an idea how to make the tool better, please let us know @webfactoryltd or email us. Thank you!
'; $table_status = $wpdb->get_results('SHOW TABLE STATUS'); if (is_array($table_status)) { foreach ($table_status as $index => $table) { if (0 !== stripos($table->Name, $wpdb->prefix)) { continue; } if (empty($table->Engine)) { continue; } $tbl_rows += $table->Rows; $tbl_size += $table->Data_length + $table->Index_length; if (in_array($table->Name, $this->core_tables)) { $tbl_core++; } else { $tbl_custom++; } } // foreach echo 'Currently used WordPress tables, prefixed with ' . $wpdb->prefix . ', consist of ' . $tbl_core . ' standard and '; if ($tbl_custom) { echo $tbl_custom . ' custom table' . ($tbl_custom == 1? '': 's'); } else { echo 'no custom tables'; } echo ' totaling ' . $this->format_size($tbl_size) .' in ' . number_format($tbl_rows) . ' rows.
'; } echo ''; echo 'There are no saved snapshots. Create a new snapshot.
'; } else { echo 'There are no saved snapshots. Create a new snapshot.
'; } echo '' . __('Keeping a plugin maintained, supported and free is neither easy nor cheap that\'s why we\'re thrilled that a premium GeoIP service decided to sponsor WP Reset. No notifications, no popups, no shady links. They keep the plugin free and clean.', 'wp-reset') . '
'; echo '' . __('IP addresses are boring and don\'t mean much to people. However, they can easily be transformed into a huge source of data by using a GeoIP service. From filtering and segmenting users to providing a better UX - geographical data can enhance any web app! See an example we recently wrote about.', 'wp-reset') . '
'; echo ''; echo '' . __('IP Geolocation knows how difficult it is to start any new project. That\'s why they offer 50,000 API requests per month for free. No credit card required, no tricks - just register for an account and you can use the service. It\'s a great way to add value to any web project.' , 'wp-reset') . '
'; echo ''; echo 'Permanently remove this tab.
'; } // tab_geoip /** * Helper function for generating UTM tagged links * * @param string $placement Optional. UTM content param. * @param string $page Optional. Page to link to. * @param array $params Optional. Extra URL params. * @param string $anchor Optional. URL anchor part. * * @return string */ function generate_web_link($placement = '', $page = '/', $params = array(), $anchor = '') { $base_url = 'https://wpreset.com'; if ('/' != $page) { $page = '/' . trim($page, '/') . '/'; } if ($page == '//') { $page = '/'; } $parts = array_merge(array('utm_source' => 'wp-reset-free', 'utm_medium' => 'plugin', 'utm_content' => $placement, 'utm_campaign' => 'wp-reset-free-v' . $this->version), $params); if (!empty($anchor)) { $anchor = '#' . trim($anchor, '#'); } $out = $base_url . $page . '?' . http_build_query($parts, '', '&') . $anchor; return $out; } // generate_web_link /** * Returns all saved snapshots from DB * * @return array */ function get_snapshots() { $snapshots = get_option('wp-reset-snapshots', array()); return $snapshots; } // get_snapshots /** * Format file size to human readable string * * @param int $bytes Size in bytes to format. * * @return string */ function format_size($bytes) { if ($bytes > 1073741824) { return number_format_i18n($bytes / 1073741824, 2) . ' GB'; } elseif ($bytes > 1048576) { return number_format_i18n($bytes / 1048576, 1) . ' MB'; } elseif ($bytes > 1024) { return number_format_i18n($bytes / 1024, 1) . ' KB'; } else { return number_format_i18n($bytes, 0) . ' bytes'; } } // format_size /** * Creates snapshot of current tables by copying them in the DB and saving metadata. * * @param int $name Optional. Name for the new snapshot. * * @return array|WP_Error Snapshot details in array on success, or error object on fail. */ function do_create_snapshot($name = '') { global $wpdb; $snapshots = $this->get_snapshots(); $snapshot = array(); $uid = $this->generate_snapshot_uid(); $tbl_core = $tbl_custom = $tbl_size = $tbl_rows = 0; if (!$uid) { return new WP_Error(1, 'Unable to generate a valid snapshot UID.'); } if ($name) { $snapshot['name'] = substr(trim($name), 0, 64); } else { $snapshot['name'] = ''; } $snapshot['uid'] = $uid; $snapshot['timestamp'] = current_time('mysql'); $table_status = $wpdb->get_results('SHOW TABLE STATUS'); if (is_array($table_status)) { foreach ($table_status as $index => $table) { if (0 !== stripos($table->Name, $wpdb->prefix)) { continue; } if (empty($table->Engine)) { continue; } $tbl_rows += $table->Rows; $tbl_size += $table->Data_length + $table->Index_length; if (in_array($table->Name, $this->core_tables)) { $tbl_core++; } else { $tbl_custom++; } $wpdb->query('OPTIMIZE TABLE ' . $table->Name); $wpdb->query('CREATE TABLE ' . $uid . '_' . $table->Name .' LIKE ' . $table->Name); $wpdb->query('INSERT ' . $uid . '_' . $table->Name . ' SELECT * FROM ' . $table->Name); } // foreach } else { return new WP_Error(1, 'Can\'t get table status data.'); } $snapshot['tbl_core'] = $tbl_core; $snapshot['tbl_custom'] = $tbl_custom; $snapshot['tbl_rows'] = $tbl_rows; $snapshot['tbl_size'] = $tbl_size; $snapshots[$uid] = $snapshot; update_option('wp-reset-snapshots', $snapshots); return $snapshot; } // create_snapshot /** * Delete snapshot metadata and tables from DB * * @param string $uid Snapshot unique 6-char ID. * * @return bool|WP_Error True on success, or error object on fail. */ function do_delete_snapshot($uid = '') { global $wpdb; $snapshots = $this->get_snapshots(); if (strlen($uid) != 6) { return new WP_Error(1, 'Invalid UID format.'); } if (!isset($snapshots[$uid])) { return new WP_Error(1, 'Unknown snapshot ID.'); } $tables = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array($uid . '\_%'))); foreach ($tables as $table) { $wpdb->query('DROP TABLE IF EXISTS ' . $table); } unset($snapshots[$uid]); update_option('wp-reset-snapshots', $snapshots); return true; } // delete_snapshot /** * Exports snapshot as SQL dump; saved in gzipped file in WP_CONTENT folder. * * @param string $uid Snapshot unique 6-char ID. * * @return string|WP_Error Export base filename, or error object on fail. */ function do_export_snapshot($uid = '') { global $wpdb; $snapshots = $this->get_snapshots(); if (strlen($uid) != 6) { return new WP_Error(1, 'Invalid snapshot ID format.'); } if (!isset($snapshots[$uid])) { return new WP_Error(1, 'Unknown snapshot ID.'); } require_once $this->plugin_dir . 'libs/dumper.php'; try { $world_dumper = Shuttle_Dumper::create(array( 'host' => DB_HOST, 'username' => DB_USER, 'password' => DB_PASSWORD, 'db_name' => DB_NAME, )); $folder = wp_mkdir_p(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder); if (!$folder) { return new WP_Error(1, 'Unable to create wp-content/' . $this->snapshots_folder . '/ folder.'); } $world_dumper->dump(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/wp-reset-snapshot-' . $uid . '.sql.gz', $uid . '_'); } catch(Shuttle_Exception $e) { return new WP_Error(1, "Couldn't dump snapshot: " . $e->getMessage()); } return 'wp-reset-snapshot-' . $uid . '.sql.gz'; } // export_snapshot /** * Replace current tables with ones in snapshot. * * @param string $uid Snapshot unique 6-char ID. * * @return bool|WP_Error True on success, or error object on fail. */ function do_restore_snapshot($uid = '') { global $wpdb; $new_tables = array(); $snapshots = $this->get_snapshots(); if (($res = $this->verify_snapshot_integrity($uid)) !== true) { return $res; } $table_status = $wpdb->get_results('SHOW TABLE STATUS'); if (is_array($table_status)) { foreach ($table_status as $index => $table) { if (0 !== stripos($table->Name, $uid . '_')) { continue; } if (empty($table->Engine)) { continue; } $new_tables[] = $table->Name; } // foreach } else { return new WP_Error(1, 'Can\'t get table status data.'); } foreach ($table_status as $index => $table) { if (0 !== stripos($table->Name, $wpdb->prefix)) { continue; } if (empty($table->Engine)) { continue; } $wpdb->query('DROP TABLE ' . $table->Name); } // foreach // copy snapshot tables to original name foreach ($new_tables as $table) { $new_name = str_replace($uid . '_', '', $table); $wpdb->query('CREATE TABLE ' . $new_name . ' LIKE ' . $table); $wpdb->query('INSERT ' . $new_name . ' SELECT * FROM ' . $table); } wp_cache_flush(); update_option('wp-reset', $this->options); update_option('wp-reset-snapshots', $snapshots); return true; } // restore_snapshot /** * Verifies snapshot integrity by comparing metadata and data in DB * * @param string $uid Snapshot unique 6-char ID. * * @return bool|WP_Error True on success, or error object on fail. */ function verify_snapshot_integrity($uid) { global $wpdb; $tbl_core = $tbl_custom = 0; $snapshots = $this->get_snapshots(); if (strlen($uid) != 6) { return new WP_Error(1, 'Invalid snapshot ID format.'); } if (!isset($snapshots[$uid])) { return new WP_Error(1, 'Unknown snapshot ID.'); } $snapshot = $snapshots[$uid]; $table_status = $wpdb->get_results('SHOW TABLE STATUS'); if (is_array($table_status)) { foreach ($table_status as $index => $table) { if (0 !== stripos($table->Name, $uid . '_')) { continue; } if (empty($table->Engine)) { continue; } if (in_array(str_replace($uid . '_', '', $table->Name), $this->core_tables)) { $tbl_core++; } else { $tbl_custom++; } } // foreach if ($tbl_core != $snapshot['tbl_core'] || $tbl_custom != $snapshot['tbl_custom']) { return new WP_Error(1, 'Snapshot data has been compromised. Saved metadata does not match data in the DB. Contact WP Reset support if data is critical, or restore it via a MySQL GUI.'); } } else { return new WP_Error(1, 'Can\'t get table status data.'); } return true; } // verify_snapshot_integrity /** * Compares a selected snapshot with the current table set in DB * * @param string $uid Snapshot unique 6-char ID. * * @return string|WP_Error Formatted table with details on success, or error object on fail. */ function do_compare_snapshots($uid) { global $wpdb; $tbl_core = $tbl_custom = 0; $current = $snapshot = array(); $out = $out2 = $out3 = ''; if (($res = $this->verify_snapshot_integrity($uid)) !== true) { return $res; } $table_status = $wpdb->get_results('SHOW TABLE STATUS'); foreach ($table_status as $index => $table) { if (empty($table->Engine)) { continue; } if (0 !== stripos($table->Name, $uid . '_') && 0 !== stripos($table->Name, $wpdb->prefix)) { continue; } $info = array(); $info['rows'] = $table->Rows; $info['size_data'] = $table->Data_length; $info['size_index'] = $table->Index_length; $schema = $wpdb->get_row('SHOW CREATE TABLE ' . $table->Name, ARRAY_N); $info['schema'] = $schema[1]; $info['engine'] = $table->Engine; $info['fullname'] = $table->Name; $basename = str_replace(array($uid . '_'), array(''), $table->Name); $info['basename'] = $basename; $info['corename'] = str_replace(array($wpdb->prefix), array(''), $basename); $info['uid'] = $uid; if (0 === stripos($table->Name, $uid . '_')) { $snapshot[$basename] = $info; } if (0 === stripos($table->Name, $wpdb->prefix)) { $info['uid'] = ''; $current[$basename] = $info; } } // foreach $in_both = array_keys(array_intersect_key($current, $snapshot)); $in_current_only = array_diff_key($current, $snapshot); $in_snapshot_only = array_diff_key($snapshot, $current); $out .= '| ' . $table['fullname'] . ' | '; $out .= 'table is not present in snapshot | '; $out .= '
| ';
$out .= ' ' . number_format($table['rows']) . ' row' . ($table['rows'] == 1? '': 's') . ' totaling ' . $this->format_size($table['size_data']) . ' in data and ' . $this->format_size($table['size_index']) . ' in index. '; $out .= '' . $table['schema'] . ''; $out .= ' | ';
$out .= ''; $out .= ' |
| table is not present in current tables | '; $out .= '' . $table['fullname'] . ' | '; $out .= '
| '; $out .= ' | ';
$out .= ' ' . number_format($table['rows']) . ' row' . ($table['rows'] == 1? '': 's') . ' totaling ' . $this->format_size($table['size_data']) . ' in data and ' . $this->format_size($table['size_index']) . ' in index. '; $out .= '' . $table['schema'] . ''; $out .= ' | ';
$out .= '
| ' . $tbl_current['fullname'] . ' | '; $out3 .= '' . $tbl_snapshot['fullname'] . ' | '; $out3 .= '
| ';
$out3 .= ' ' . number_format($tbl_current['rows']) . ' rows totaling ' . $this->format_size($tbl_current['size_data']) . ' in data and ' . $this->format_size($tbl_current['size_index']) . ' in index. '; $out3 .= '' . $tbl_current['schema'] . ''; $out3 .= ' | ';
$out3 .= '';
$out3 .= ' ' . number_format($tbl_snapshot['rows']) . ' rows totaling ' . $this->format_size($tbl_snapshot['size_data']) . ' in data and ' . $this->format_size($tbl_snapshot['size_index']) . ' in index. '; $out3 .= '' . $tbl_snapshot['schema'] . ''; $out3 .= ' | ';
$out3 .= '
| ' . $tbl_current['fullname'] . ' table schemas do not match | '; $out2 .= '' . $tbl_snapshot['fullname'] . ' table schemas do not match | '; $out2 .= '
| ';
$out2 .= ' ' . number_format($tbl_current['rows']) . ' rows totaling ' . $this->format_size($tbl_current['size_data']) . ' in data and ' . $this->format_size($tbl_current['size_index']) . ' in index. '; $out2 .= ' | ';
$out2 .= '';
$out2 .= ' ' . number_format($tbl_snapshot['rows']) . ' rows totaling ' . $this->format_size($tbl_snapshot['size_data']) . ' in data and ' . $this->format_size($tbl_snapshot['size_index']) . ' in index. '; $out2 .= ' | ';
$out2 .= '
| '; $out2 .= $diff->Render($renderer); $out2 .= ' | '; $out2 .= '|
| ' . $tbl_current['fullname'] . ' data in tables does not match | '; $out2 .= '' . $tbl_snapshot['fullname'] . ' data in tables does not match | '; $out2 .= '||||||||||||
| ';
$out2 .= ' ' . number_format($tbl_current['rows']) . ' rows totaling ' . $this->format_size($tbl_current['size_data']) . ' in data and ' . $this->format_size($tbl_current['size_index']) . ' in index. '; $out2 .= ' | ';
$out2 .= '';
$out2 .= ' ' . number_format($tbl_snapshot['rows']) . ' rows totaling ' . $this->format_size($tbl_snapshot['size_data']) . ' in data and ' . $this->format_size($tbl_snapshot['size_index']) . ' in index. '; $out2 .= ' | ';
$out2 .= '||||||||||||
';
if ($tbl_current['corename'] == 'options') {
$ss_prefix = $tbl_snapshot['uid'] . '_' . $wpdb->prefix;
$diff_rows = $wpdb->get_results("SELECT {$wpdb->prefix}options.option_name, {$wpdb->prefix}options.option_value AS current_value, {$ss_prefix}options.option_value AS snapshot_value FROM {$wpdb->prefix}options LEFT JOIN {$ss_prefix}options ON {$ss_prefix}options.option_name = {$wpdb->prefix}options.option_name WHERE {$wpdb->prefix}options.option_value != {$ss_prefix}options.option_value LIMIT 100;");
$only_current = $wpdb->get_results("SELECT {$wpdb->prefix}options.option_name, {$wpdb->prefix}options.option_value AS current_value, {$ss_prefix}options.option_value AS snapshot_value FROM {$wpdb->prefix}options LEFT JOIN {$ss_prefix}options ON {$ss_prefix}options.option_name = {$wpdb->prefix}options.option_name WHERE {$ss_prefix}options.option_value IS NULL LIMIT 100;");
$only_snapshot = $wpdb->get_results("SELECT {$wpdb->prefix}options.option_name, {$wpdb->prefix}options.option_value AS current_value, {$ss_prefix}options.option_value AS snapshot_value FROM {$wpdb->prefix}options LEFT JOIN {$ss_prefix}options ON {$ss_prefix}options.option_name = {$wpdb->prefix}options.option_name WHERE {$wpdb->prefix}options.option_value IS NULL LIMIT 100;");
$out2 .= '
Detailed data diff is not available for this table. '; } $out2 .= ' | ';
$out2 .= '|||||||||||||