PluginProbe
WP Reset / 2.05
WP Reset v2.05
trunk 1.0 1.1 1.11 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.77 1.80 1.81 1.82 1.83 1.84 1.85 1.86 1.90 All 42 releases
← All changes | wp-reset.php +2821 -1504 1.452.05 View file →
@@ -1,15 +1,19 @@
1 1 <?php
2 2 /*
3 3 Plugin Name: WP Reset
4 4 Plugin URI: https://wpreset.com/
5 - Description: Reset the site to default installation values without modifying any files. Deletes all customizations and content.
6 - Version: 1.45
5 + Description: Reset the entire site or just selected parts while reserving the option to undo by using snapshots.
6 + Version: 2.05
7 + Requires at least: 4.0
8 + Requires PHP: 5.2
9 + Tested up to: 6.8
10 + License: GPLv2 or later
7 11 Author: WebFactory Ltd
8 12 Author URI: https://www.webfactoryltd.com/
9 13 Text Domain: wp-reset
10 14
11 - Copyright 2015 - 2018 Web factory Ltd (email: wpreset@webfactoryltd.com)
15 + Copyright 2015 - 2025 WebFactory Ltd (email: wpreset@webfactoryltd.com)
12 16
13 17 This program is free software; you can redistribute it and/or modify
14 18 it under the terms of the GNU General Public License, version 2, as
15 19 published by the Free Software Foundation.
@@ -25,1837 +29,3150 @@
25 29 */
26 30
27 31 // include only file
28 32 if (!defined('ABSPATH')) {
29 - wp_die(__('Do not open this file directly.', 'wp-error'));
33 + die('Do not open this file directly.');
30 34 }
31 35
32 36
37 +define('WP_RESET_FILE', __FILE__);
38 +
39 +require_once dirname(__FILE__) . '/wp-reset-utility.php';
40 +require_once dirname(__FILE__) . '/wp-reset-licensing.php';
41 +
42 +require_once dirname(__FILE__) . '/wf-flyout/wf-flyout.php';
43 +new wf_flyout(__FILE__);
44 +
33 45 // load WP-CLI commands, if needed
34 46 if (defined('WP_CLI') && WP_CLI) {
35 - require_once dirname( __FILE__ ) . '/wp-reset-cli.php';
47 + require_once dirname(__FILE__) . '/wp-reset-cli.php';
36 48 }
37 49
38 50
39 -class WP_Reset {
40 - protected static $instance = null;
41 - public $version = 0;
42 - public $plugin_url = '';
43 - public $plugin_dir = '';
44 - public $snapshots_folder = 'wp-reset-snapshots-export';
45 - protected $options = array();
46 - private $delete_count = 0;
47 - private $core_tables = array('commentmeta', 'comments', 'links', 'options', 'postmeta', 'posts', 'term_relationships', 'term_taxonomy', 'termmeta', 'terms', 'usermeta', 'users');
51 +class WP_Reset
52 +{
53 + protected static $instance = null;
54 + public $version = 0;
55 + public $plugin_url = '';
56 + public $plugin_dir = '';
57 + public $snapshots_folder = 'wp-reset-snapshots-export';
58 + protected $options = array();
59 + private $delete_count = 0;
60 + private $licensing_servers = array('https://dashboard.wpreset.com/api/');
61 + public $core_tables = array('commentmeta', 'comments', 'links', 'options', 'postmeta', 'posts', 'term_relationships', 'term_taxonomy', 'termmeta', 'terms', 'usermeta', 'users');
62 + private $license = null;
63 + public $filesystem_initialized = false;
48 64
65 + /**
66 + * Creates a new WP_Reset object and implements singleton
67 + *
68 + * @return WP_Reset
69 + */
70 + static function getInstance()
71 + {
72 + if (!is_a(self::$instance, 'WP_Reset')) {
73 + self::$instance = new WP_Reset();
74 + }
49 75
50 - /**
51 - * Creates a new WP_Reset object and implements singleton
52 - *
53 - * @return WP_Reset
54 - */
55 - static function getInstance() {
56 - if (!is_a(self::$instance, 'WP_Reset')) {
57 - self::$instance = new WP_Reset();
58 - }
76 + return self::$instance;
77 + } // getInstance
59 78
60 - return self::$instance;
61 - } // getInstance
62 79
80 + /**
81 + * Initialize properties, hook to filters and actions
82 + *
83 + * @return null
84 + */
85 + private function __construct()
86 + {
87 + $this->version = $this->get_plugin_version();
88 + $this->plugin_dir = plugin_dir_path(__FILE__);
89 + $this->plugin_url = plugin_dir_url(__FILE__);
90 + $this->load_options();
63 91
64 - /**
65 - * Initialize properties, hook to filters and actions
66 - *
67 - * @return null
68 - */
69 - private function __construct() {
70 - $this->version = $this->get_plugin_version();
71 - $this->plugin_dir = plugin_dir_path(__FILE__);
72 - $this->plugin_url = plugin_dir_url(__FILE__);
73 - $this->load_options();
92 + $this->license = new WF_Licensing(array(
93 + 'prefix' => 'wpr',
94 + 'licensing_servers' => $this->licensing_servers,
95 + 'version' => $this->version,
96 + 'plugin_file' => __FILE__,
97 + 'plugin_page' => 'tools_page_wp-reset',
98 + 'skip_hooks' => false,
99 + 'debug' => true,
100 + 'js_folder' => plugin_dir_url(__FILE__) . '/js/'
101 + ));
74 102
75 - add_action('admin_menu', array($this, 'admin_menu'));
76 - add_action('admin_init', array($this, 'do_all_actions'));
77 - add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
78 - add_action('wp_ajax_wp_reset_dismiss_notice', array($this, 'ajax_dismiss_notice'));
79 - add_action('wp_ajax_wp_reset_run_tool', array($this, 'ajax_run_tool'));
103 + add_action('admin_menu', array($this, 'admin_menu'));
104 + add_action('admin_init', array($this, 'do_all_actions'));
105 + add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
106 + add_action('admin_action_wpr_dismiss_notice', array($this, 'action_dismiss_notice'));
107 + add_action('wp_ajax_wp_reset_dismiss_notice', array($this, 'ajax_dismiss_notice'));
108 + add_action('wp_ajax_wp_reset_run_tool', array($this, 'ajax_run_tool'));
109 + add_action('admin_print_scripts', array($this, 'remove_admin_notices'));
110 + add_action('admin_action_wpr_install_wpfssl', array($this, 'install_wpfssl'));
80 111
81 - add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'));
82 - add_filter('plugin_row_meta', array($this, 'plugin_meta_links'), 10, 2);
83 - add_filter('admin_footer_text', array($this, 'admin_footer_text'));
84 - add_filter('install_plugins_table_api_args_featured', array($this, 'featured_plugins_tab'));
112 + add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'));
113 + add_filter('plugin_row_meta', array($this, 'plugin_meta_links'), 10, 2);
114 + add_filter('admin_footer_text', array($this, 'admin_footer_text'));
85 115
86 - $this->core_tables = array_map(function($tbl) { global $wpdb; return $wpdb->prefix . $tbl; }, $this->core_tables);
87 - } // __construct
116 + $this->core_tables = array_map(function ($tbl) {
117 + global $wpdb;
118 + return $wpdb->prefix . $tbl;
119 + }, $this->core_tables);
120 + } // __construct
88 121
89 122
90 - /**
91 - * Get plugin version from file header
92 - *
93 - * @return string
94 - */
95 - function get_plugin_version() {
96 - $plugin_data = get_file_data(__FILE__, array('version' => 'Version'), 'plugin');
123 + /**
124 + * Get plugin version from file header
125 + *
126 + * @return string
127 + */
128 + function get_plugin_version()
129 + {
130 + $plugin_data = get_file_data(__FILE__, array('version' => 'Version'), 'plugin');
97 131
98 - return $plugin_data['version'];
99 - } // get_plugin_version
132 + return $plugin_data['version'];
133 + } // get_plugin_version
100 134
101 135
102 - /**
103 - * Load and prepare the options array
104 - * If needed create a new DB entry
105 - *
106 - * @return array
107 - */
108 - private function load_options() {
109 - $options = get_option('wp-reset', array());
110 - $change = false;
136 + /**
137 + * Load and prepare the options array
138 + * If needed create a new DB entry
139 + *
140 + * @return array
141 + */
142 + private function load_options()
143 + {
144 + $options = get_option('wp-reset', array());
145 + $change = false;
111 146
112 - if (!isset($options['meta'])) {
113 - $options['meta'] = array('first_version' => $this->version, 'first_install' => current_time('timestamp', true), 'reset_count' => 0);
114 - $change = true;
115 - }
116 - if (!isset($options['dismissed_notices'])) {
117 - $options['dismissed_notices'] = array();
118 - $change = true;
119 - }
120 - if (!isset($options['last_run'])) {
121 - $options['last_run'] = array();
122 - $change = true;
123 - }
124 - if (!isset($options['options'])) {
125 - $options['options'] = array();
126 - $change = true;
127 - }
128 - if ($change) {
129 - update_option('wp-reset', $options, true);
130 - }
147 + if (!isset($options['meta'])) {
148 + $options['meta'] = array('first_version' => $this->version, 'first_install' => current_time('timestamp', true), 'reset_count' => 0);
149 + $change = true;
150 + }
151 + if (!isset($options['dismissed_notices'])) {
152 + $options['dismissed_notices'] = array();
153 + $change = true;
154 + }
155 + if (!isset($options['last_run'])) {
156 + $options['last_run'] = array();
157 + $change = true;
158 + }
159 + if (!isset($options['options'])) {
160 + $options['options'] = array();
161 + $change = true;
162 + }
163 + if ($change) {
164 + update_option('wp-reset', $options, true);
165 + }
131 166
132 - $this->options = $options;
133 - return $options;
134 - } // load_options
167 + $this->options = $options;
168 + return $options;
169 + } // load_options
135 170
136 171
137 - /**
138 - * Get meta part of plugin options
139 - *
140 - * @return array
141 - */
142 - function get_meta() {
143 - return $this->options['meta'];
144 - } // get_meta
172 + /**
173 + * Get meta part of plugin options
174 + *
175 + * @return array
176 + */
177 + function get_meta()
178 + {
179 + return $this->options['meta'];
180 + } // get_meta
145 181
146 182
147 - /**
148 - * Get all dismissed notices, or check for one specific notice
149 - *
150 - * @param string $notice_name Optional. Check if specified notice is dismissed.
151 - *
152 - * @return bool|array
153 - */
154 - function get_dismissed_notices($notice_name = '') {
155 - $notices = $this->options['dismissed_notices'];
183 + /**
184 + * Get all dismissed notices, or check for one specific notice
185 + *
186 + * @param string $notice_name Optional. Check if specified notice is dismissed.
187 + *
188 + * @return bool|array
189 + */
190 + function get_dismissed_notices($notice_name = '')
191 + {
192 + $notices = $this->options['dismissed_notices'];
156 193
157 - if (empty($notice_name)) {
158 - return $notices;
159 - } else {
160 - if (empty($notices[$notice_name])) {
161 - return false;
162 - } else {
194 + if (empty($notice_name)) {
195 + return $notices;
196 + } else {
197 + if (empty($notices[$notice_name])) {
198 + return false;
199 + } else {
200 + return true;
201 + }
202 + }
203 + } // get_dismissed_notices
204 +
205 +
206 + /**
207 + * Get options part of plugin options
208 + *
209 + * @param string $key Optional.
210 + *
211 + * @return array
212 + */
213 + function get_options()
214 + {
215 + return $this->options['options'];
216 + } // get_options
217 +
218 +
219 + /**
220 + * Update specified plugin options key
221 + *
222 + * @param string $key Data to save.
223 + * @param string $data Option key.
224 + *
225 + * @return bool
226 + */
227 + function update_options($key, $data)
228 + {
229 + if (false === in_array($key, array('meta', 'license', 'dismissed_notices', 'options'))) {
230 + user_error('Unknown options key.', E_USER_ERROR);
231 + return false;
232 + }
233 +
234 + $this->options[$key] = $data;
235 + $tmp = update_option('wp-reset', $this->options);
236 +
237 + return $tmp;
238 + } // update_options
239 +
240 +
241 + /**
242 + * Add plugin menu entry under Tools menu
243 + *
244 + * @return null
245 + */
246 + function admin_menu()
247 + {
248 + add_management_page(__('WP Reset', 'wp-reset'), __('WP Reset', 'wp-reset'), 'administrator', 'wp-reset', array($this, 'plugin_page'));
249 + } // admin_menu
250 +
251 +
252 + /**
253 + * Dismiss notice via AJAX call
254 + *
255 + * @return null
256 + */
257 + function ajax_dismiss_notice()
258 + {
259 + check_ajax_referer('wp-reset_dismiss_notice');
260 +
261 + if (!current_user_can('manage_options')) {
262 + wp_send_json_error(__('You are not allowed to run this action.', 'wp-reset'));
263 + }
264 +
265 + if (!isset($_GET['notice_name'])) {
266 + wp_send_json_error(__('Unknown Notice.', 'wp-reset'));
267 + }
268 +
269 + $notice_name = trim(sanitize_text_field(wp_unslash($_GET['notice_name'])));
270 + if (!$this->dismiss_notice($notice_name)) {
271 + wp_send_json_error(__('Notice is already dismissed.', 'wp-reset'));
272 + } else {
273 + wp_send_json_success();
274 + }
275 + } // ajax_dismiss_notice
276 +
277 +
278 + /**
279 + * Dismiss notice via admin action
280 + *
281 + * @return null
282 + */
283 + function action_dismiss_notice()
284 + {
285 + if (!isset($_GET['notice']) || !isset($_GET['_wpnonce']) || false == wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'wpr_dismiss_notice')) {
286 + wp_die('Please reload the page and try again.');
287 + }
288 +
289 + if (empty($_GET['notice'])) {
290 + wp_safe_redirect(admin_url());
291 + exit;
292 + }
293 +
294 + $notice_name = trim(sanitize_text_field(wp_unslash($_GET['notice'])));
295 + $this->dismiss_notice($notice_name);
296 +
297 + if (!empty($_GET['redirect'])) {
298 + wp_safe_redirect(sanitize_url(wp_unslash($_GET['redirect'])));
299 + } else {
300 + wp_safe_redirect(admin_url());
301 + }
302 +
303 + exit;
304 + } // action_dismiss_notice
305 +
306 +
307 + /**
308 + * Dismiss notice by adding it to dismissed_notices options array
309 + *
310 + * @param string $notice_name Notice to dismiss.
311 + *
312 + * @return bool
313 + */
314 + function dismiss_notice($notice_name)
315 + {
316 + if ($this->get_dismissed_notices($notice_name)) {
317 + return false;
318 + } else {
319 + $notices = $this->get_dismissed_notices();
320 + $notices[$notice_name] = true;
321 + $this->update_options('dismissed_notices', $notices);
322 + return true;
323 + }
324 + } // dismiss_notice
325 +
326 +
327 + /**
328 + * Returns all WP pointers
329 + *
330 + * @return array
331 + */
332 + function get_pointers()
333 + {
334 + $pointers = array();
335 +
336 + $pointers['welcome'] = array('target' => '#menu-tools', 'edge' => 'left', 'align' => 'right', 'content' => 'Thank you for installing the <b style="font-weight: 800;">WP Reset</b> plugin!<br>Open <a href="' . esc_url(admin_url('tools.php?page=wp-reset')) . '">Tools - WP Reset</a> to access resetting tools and start developing &amp; debugging faster.');
337 +
338 + return $pointers;
339 + } // get_pointers
340 +
341 +
342 + /**
343 + * Enqueue CSS and JS files
344 + *
345 + * @return null
346 + */
347 + function admin_enqueue_scripts($hook)
348 + {
349 + // welcome pointer is shown on all pages except WPR to admins, until dismissed
350 + $pointers = $this->get_pointers();
351 + $dismissed_notices = $this->get_dismissed_notices();
352 +
353 + foreach ($dismissed_notices as $notice_name => $tmp) {
354 + if ($tmp) {
355 + unset($pointers[$notice_name]);
356 + }
357 + } // foreach
358 +
359 + if (!empty($pointers) && !$this->is_plugin_page() && current_user_can('manage_options')) {
360 + $pointers['_nonce_dismiss_pointer'] = wp_create_nonce('wp-reset_dismiss_notice');
361 +
362 + wp_enqueue_style('wp-pointer');
363 +
364 + wp_enqueue_script('wp-reset-pointers', $this->plugin_url . 'js/wp-reset-pointers.js', array('jquery'), $this->version, true);
365 + wp_enqueue_script('wp-pointer');
366 + wp_localize_script('wp-pointer', 'wp_reset_pointers', $pointers);
367 + }
368 +
369 + // exit early if not on WP Reset page
370 + if (!$this->is_plugin_page()) {
371 + return;
372 + }
373 +
374 + $js_localize = array(
375 + 'undocumented_error' => __('An undocumented error has occurred. Please refresh the page and try again.', 'wp-reset'),
376 + 'documented_error' => __('An error has occurred.', 'wp-reset'),
377 + 'plugin_name' => __('WP Reset', 'wp-reset'),
378 + 'settings_url' => admin_url('tools.php?page=wp-reset'),
379 + 'wpfssl_install_url' => add_query_arg(array('action' => 'wpr_install_wpfssl', '_wpnonce' => wp_create_nonce('install_wpfssl'), 'rnd' => wp_rand()), admin_url('admin.php')),
380 + 'icon_url' => $this->plugin_url . 'img/wp-reset-icon.png',
381 + 'invalid_confirmation' => __('Please type "reset" in the confirmation field.', 'wp-reset'),
382 + 'invalid_confirmation_title' => __('Invalid confirmation', 'wp-reset'),
383 + 'cancel_button' => __('Cancel', 'wp-reset'),
384 + 'ok_button' => __('OK', 'wp-reset'),
385 + 'confirm_button' => __('Reset WordPress', 'wp-reset'),
386 + 'confirm_title' => __('Are you sure you want to proceed?', 'wp-reset'),
387 + 'confirm_title_reset' => __('Are you sure you want to reset the site?', 'wp-reset'),
388 + 'confirm1' => __('Clicking "Reset WordPress" will reset your site to default values. All content will be lost. Always <a href="#" class="create-new-snapshot" data-description="Before resetting the site">create a snapshot</a> if you want to be able to undo.</b>', 'wp-reset'),
389 + 'confirm2' => __('Click "Cancel" to abort.', 'wp-reset'),
390 + 'doing_reset' => __('Resetting in progress. Please wait.', 'wp-reset'),
391 + 'snapshot_success' => __('Snapshot created', 'wp-reset'),
392 + 'snapshot_wait' => __('Creating snapshot. Please wait.', 'wp-reset'),
393 + 'snapshot_confirm' => __('Create snapshot', 'wp-reset'),
394 + 'snapshot_placeholder' => __('Snapshot name or brief description, ie: before plugin install', 'wp-reset'),
395 + 'snapshot_text' => __('Enter snapshot name or brief description', 'wp-reset'),
396 + 'snapshot_title' => __('Create a new snapshot', 'wp-reset'),
397 + 'nonce_dismiss_notice' => wp_create_nonce('wp-reset_dismiss_notice'),
398 + 'activating' => __('Activating', 'wp-reset'),
399 + 'deactivating' => __('Deactivating', 'wp-reset'),
400 + 'deleting' => __('Deleting', 'wp-reset'),
401 + 'installing' => __('Installing', 'wp-reset'),
402 + 'activate_failed' => __('Could not activate', 'wp-reset'),
403 + 'deactivate_failed' => __('Could not deactivate', 'wp-reset'),
404 + 'delete_failed' => __('Could not delete', 'wp-reset'),
405 + 'install_failed' => __('Could not install', 'wp-reset'),
406 + 'install_failed_existing' => __('is already installed', 'wp-reset'),
407 + 'nonce_run_tool' => wp_create_nonce('wp-reset_run_tool'),
408 + 'nonce_do_reset' => wp_create_nonce('wp-reset_do_reset'),
409 + );
410 +
411 + wp_enqueue_style('plugin-install');
412 + wp_enqueue_style('wp-jquery-ui-dialog');
413 + wp_enqueue_style('wp-reset', $this->plugin_url . 'css/wp-reset.css', array(), $this->version);
414 + wp_enqueue_style('wp-reset-sweetalert2', $this->plugin_url . 'css/sweetalert2.min.css', array(), $this->version);
415 + wp_enqueue_style('wp-reset-tooltipster', $this->plugin_url . 'css/tooltipster.bundle.min.css', array(), $this->version);
416 +
417 + wp_enqueue_script('plugin-install');
418 + wp_enqueue_script('jquery-ui-tabs');
419 + wp_enqueue_script('jquery-ui-dialog');
420 + wp_enqueue_script('wp-reset-sweetalert2', $this->plugin_url . 'js/wp-reset-libs.min.js', array('jquery'), $this->version, true);
421 + wp_enqueue_script('wp-reset', $this->plugin_url . 'js/wp-reset.js', array('jquery'), $this->version, true);
422 + wp_localize_script('wp-reset', 'wp_reset', $js_localize);
423 +
424 + add_thickbox();
425 +
426 + // fix for aggressive plugins that include their CSS on all pages
427 + wp_dequeue_style('uiStyleSheet');
428 + wp_dequeue_style('wpcufpnAdmin');
429 + wp_dequeue_style('unifStyleSheet');
430 + wp_dequeue_style('wpcufpn_codemirror');
431 + wp_dequeue_style('wpcufpn_codemirrorTheme');
432 + wp_dequeue_style('collapse-admin-css');
433 + wp_dequeue_style('jquery-ui-css');
434 + wp_dequeue_style('tribe-common-admin');
435 + wp_dequeue_style('file-manager__jquery-ui-css');
436 + wp_dequeue_style('file-manager__jquery-ui-css-theme');
437 + wp_dequeue_style('wpmegmaps-jqueryui');
438 + wp_dequeue_style('wp-botwatch-css');
439 + wp_dequeue_style('uap_main_admin_style');
440 + wp_dequeue_style('uap_font_awesome');
441 + wp_dequeue_style('uap_jquery-ui.min.css');
442 + } // admin_enqueue_scripts
443 +
444 +
445 + /**
446 + * Remove all WP notices on WPR page
447 + *
448 + * @return null
449 + */
450 + function remove_admin_notices()
451 + {
452 + if (!$this->is_plugin_page()) {
453 + return false;
454 + }
455 +
456 + global $wp_filter;
457 + unset($wp_filter['user_admin_notices'], $wp_filter['admin_notices']);
458 + } // remove_admin_notices
459 +
460 +
461 + /**
462 + * Check if WP-CLI is available and running
463 + *
464 + * @return bool
465 + */
466 + static function is_cli_running()
467 + {
468 + if (!is_null($value = apply_filters('wp-reset-override-is-cli-running', null))) {
469 + return (bool) $value;
470 + }
471 +
472 + if (defined('WP_CLI') && WP_CLI) {
473 + return true;
474 + } else {
475 + return false;
476 + }
477 + } // is_cli_running
478 +
479 +
480 + /**
481 + * Check if given plugin is installed
482 + *
483 + * @param [string] $slug Plugin slug
484 + * @return boolean
485 + */
486 + function is_plugin_installed($slug)
487 + {
488 + if (!function_exists('get_plugins')) {
489 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
490 + }
491 + $all_plugins = get_plugins();
492 +
493 + if (!empty($all_plugins[$slug])) {
494 + return true;
495 + } else {
496 + return false;
497 + }
498 + } // is_plugin_installed
499 +
500 +
501 + /**
502 + * Deletes all transients.
503 + *
504 + * @return int Number of deleted transient DB entries
505 + */
506 + function do_delete_transients()
507 + {
508 + global $wpdb;
509 +
510 + // phpcs:ignore db call warning as we are using uncommon queries
511 + $count = $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '\_transient\_%' OR option_name LIKE '\_site\_transient\_%'"); // phpcs:ignore
512 +
513 + wp_cache_flush();
514 +
515 + do_action('wp_reset_delete_transients', $count);
516 +
517 + return $count;
518 + } // do_delete_transients
519 +
520 +
521 + /**
522 + * Purge all cache for popular caching plugins
523 + *
524 + * @return bool true
525 + */
526 + function do_purge_cache()
527 + {
528 + global $wp_reset;
529 +
530 + wp_cache_flush();
531 + $wp_reset->do_delete_transients();
532 +
533 + if (function_exists('w3tc_flush_all')) {
534 + w3tc_flush_all();
535 + }
536 + if (function_exists('wp_cache_clear_cache')) {
537 + wp_cache_clear_cache();
538 + }
539 + if (method_exists('LiteSpeed_Cache_API', 'purge_all')) {
540 + LiteSpeed_Cache_API::purge_all();
541 + }
542 + if (class_exists('Endurance_Page_Cache')) {
543 + $epc = new Endurance_Page_Cache;
544 + $epc->purge_all();
545 + }
546 + if (class_exists('SG_CachePress_Supercacher') && method_exists('SG_CachePress_Supercacher', 'purge_cache')) {
547 + SG_CachePress_Supercacher::purge_cache(true);
548 + }
549 + if (class_exists('SiteGround_Optimizer\Supercacher\Supercacher')) {
550 + SiteGround_Optimizer\Supercacher\Supercacher::purge_cache();
551 + }
552 + if (isset($GLOBALS['wp_fastest_cache']) && method_exists($GLOBALS['wp_fastest_cache'], 'deleteCache')) {
553 + $GLOBALS['wp_fastest_cache']->deleteCache(true);
554 + }
555 + if (is_callable(array('Swift_Performance_Cache', 'clear_all_cache'))) {
556 + Swift_Performance_Cache::clear_all_cache();
557 + }
558 + if (is_callable(array('Hummingbird\WP_Hummingbird', 'flush_cache'))) {
559 + Hummingbird\WP_Hummingbird::flush_cache(true, false);
560 + }
561 +
562 + do_action('wp_reset_purge_cache');
563 +
163 564 return true;
164 - }
165 - }
166 - } // get_dismissed_notices
565 + } // do_purge_cache
167 566
168 567
169 - /**
170 - * Get options part of plugin options
171 - *
172 - * todo: not completed
173 - *
174 - * @param string $key Optional.
175 - *
176 - * @return array
177 - */
178 - function get_options($key = '') {
179 - return $this->options['options'];
180 - } // get_options
568 + /**
569 + * Resets all theme options (mods).
570 + *
571 + * @param bool $all_themes Delete mods for all themes or just the current one
572 + *
573 + * @return int Number of deleted mod DB entries
574 + */
575 + function do_reset_theme_options($all_themes = true)
576 + {
577 + global $wpdb;
181 578
579 + // phpcs:ignore db call warning as we are using uncommon queries
580 + $count = $wpdb->query($wpdb->prepare("DELETE FROM $wpdb->options WHERE option_name LIKE %s OR option_name LIKE %s", array('mods\_%', 'theme_mods\_%'))); // phpcs:ignore
182 581
183 - /**
184 - * Update plugin options, currently entire array
185 - *
186 - * todo: this handles the entire options array although it should only do the options part - it's confusing
187 - *
188 - * @param string $key Data to save.
189 - * @param string $data Option key.
190 - *
191 - * @return bool
192 - */
193 - function update_options($key, $data) {
194 - $this->options[$key] = $data;
195 - $tmp = update_option('wp-reset', $this->options);
582 + do_action('wp_reset_reset_theme_options', $count);
196 583
197 - return $tmp;
198 - } // set_options
584 + return $count;
585 + } // do_reset_theme_options
199 586
200 587
201 - /**
202 - * Add plugin menu entry under Tools menu
203 - *
204 - * @return null
205 - */
206 - function admin_menu() {
207 - add_management_page(__('WP Reset', 'wp-reset'), __('WP Reset', 'wp-reset'), 'administrator', 'wp-reset', array($this, 'plugin_page'));
208 - } // admin_menu
588 + /**
589 + * Deletes all files in uploads folder.
590 + *
591 + * @return int Number of deleted files and folders.
592 + */
593 + function do_delete_uploads()
594 + {
595 + global $wp_filesystem;
596 + $this->wp_init_filesystem();
597 +
598 + $upload_dir = wp_get_upload_dir();
599 + $this->delete_count = 0;
209 600
601 + if ( $wp_filesystem->is_dir( $upload_dir['basedir'] ) ) {
210 602
211 - /**
212 - * Dismiss notice via AJAX call
213 - *
214 - * @return null
215 - */
216 - function ajax_dismiss_notice() {
217 - check_ajax_referer('wp-reset_dismiss_notice');
603 + // Get all files and directories in the folder.
604 + $files = $wp_filesystem->dirlist( $upload_dir['basedir'] );
605 +
606 + // Loop through the files and delete them.
607 + foreach ( $files as $file => $details ) {
608 + $file_path = trailingslashit( $upload_dir['basedir'] ) . $file;
609 + $wp_filesystem->delete( $file_path, true );
610 + $this->delete_count++;
611 + }
612 + }
218 613
219 - $notice_name = trim(@$_GET['notice_name']);
220 - if (!$this->dismiss_notice($notice_name)) {
221 - wp_send_json_error('Notice is already dismissed.');
222 - } else {
223 - wp_send_json_success();
224 - }
225 - } // ajax_dismiss_notice
614 + do_action('wp_reset_delete_uploads', $this->delete_count);
226 615
616 + return $this->delete_count;
617 + } // do_delete_uploads
227 618
228 - /**
229 - * Dismiss notice by adding it to dismissed_notices options array
230 - *
231 - * @param string $notice_name Notice to dismiss.
232 - *
233 - * @return bool
234 - */
235 - function dismiss_notice($notice_name) {
236 - if ($this->get_dismissed_notices($notice_name)) {
237 - return false;
238 - } else {
239 - $notices = $this->get_dismissed_notices();
240 - $notices[$notice_name] = true;
241 - $this->update_options('dismissed_notices', $notices);
242 - return true;
243 - }
244 - } // dismiss_notice
619 + /**
620 + * Deactivate all plugins
621 + *
622 + * @param array keep_wp_reset - Keep WP Reset active and installed, silent_deactivate - Skip individual plugin deactivation functions when deactivating
623 + *
624 + * @return int Number of deactivated plugins.
625 + */
626 + function do_deactivate_plugins($params = array())
627 + {
628 + if (!function_exists('get_plugins')) {
629 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
630 + }
631 + if (!function_exists('request_filesystem_credentials')) {
632 + require_once ABSPATH . 'wp-admin/includes/file.php';
633 + }
245 634
635 + $wp_reset_basename = plugin_basename(WP_RESET_FILE);
636 + $params = shortcode_atts(array('keep_wp_reset' => true, 'silent_deactivate' => false), (array) $params);
246 637
247 - /**
248 - * Returns all WP pointers
249 - *
250 - * @return array
251 - */
252 - function get_pointers() {
253 - $pointers = array();
638 + $active_plugins = (array) get_option('active_plugins', array());
639 + if ($params['keep_wp_reset']) {
640 + if (($key = array_search($wp_reset_basename, $active_plugins)) !== false) {
641 + unset($active_plugins[$key]);
642 + }
643 + }
254 644
255 - $pointers['welcome'] = array('target' => '#menu-tools', 'edge' => 'left', 'align' => 'right', 'content' => 'Thank you for installing the <b style="font-weight: 800;">WP Reset</b> plugin!<br>Open <a href="' . admin_url('tools.php?page=wp-reset'). '">Tools - WP Reset</a> to access resetting tools and start developing &amp; debugging faster.');
645 + if (!empty($active_plugins)) {
646 + deactivate_plugins($active_plugins, $params['silent_deactivate'], false);
647 + }
256 648
257 - return $pointers;
258 - } // get_pointers
649 + do_action('wp_reset_deactivate_plugins', $active_plugins, $params);
259 650
651 + return sizeof($active_plugins);
652 + } // do_deactivate_plugins
260 653
261 - /**
262 - * Enqueue CSS and JS files
263 - *
264 - * @return null
265 - */
266 - function admin_enqueue_scripts($hook) {
267 - // welcome pointer is shown on all pages except WPR, untill dismissed
268 - $pointers = $this->get_pointers();
269 - $dismissed_notices = $this->get_dismissed_notices();
270 654
271 - foreach ($dismissed_notices as $notice_name => $tmp) {
272 - if ($tmp) {
273 - unset($pointers[$notice_name]);
274 - }
275 - } // foreach
655 + /**
656 + * Delete all plugins
657 + *
658 + * @param array keep_wp_reset - Keep WP Reset active and installed
659 + *
660 + * @return int Number of deleted plugins.
661 + */
662 + function do_delete_plugins($params = array())
663 + {
664 + if (!function_exists('get_plugins')) {
665 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
666 + }
667 + if (!function_exists('request_filesystem_credentials')) {
668 + require_once ABSPATH . 'wp-admin/includes/file.php';
669 + }
276 670
277 - if (!empty($pointers) && 'tools_page_wp-reset' != $hook) {
278 - $pointers['_nonce_dismiss_pointer'] = wp_create_nonce('wp-reset_dismiss_notice');
671 + $wp_reset_basename = plugin_basename(WP_RESET_FILE);
672 + $params = shortcode_atts(array('keep_wp_reset' => true), (array) $params);
279 673
280 - wp_enqueue_style('wp-pointer');
674 + $all_plugins = get_plugins();
675 + if ($params['keep_wp_reset']) {
676 + unset($all_plugins[$wp_reset_basename]);
677 + }
281 678
282 - wp_enqueue_script('wp-reset-pointers', $this->plugin_url . 'js/wp-reset-pointers.js', array('jquery'), $this->version, true);
283 - wp_enqueue_script('wp-pointer');
284 - wp_localize_script('wp-pointer', 'wp_reset_pointers', $pointers);
285 - }
679 + if (!empty($all_plugins)) {
680 + delete_plugins(array_keys($all_plugins));
681 + }
286 682
287 - // exit early if not on WP Reset page
288 - if ('tools_page_wp-reset' != $hook) {
289 - return;
290 - }
683 + do_action('wp_reset_delete_plugins', $all_plugins, $params);
291 684
292 - $options = $this->get_options();
685 + return sizeof($all_plugins);
686 + } // do_delete_plugins
293 687
294 - $js_localize = array('undocumented_error' => __('An undocumented error has occured. Please refresh the page and try again.', 'wp-reset'),
295 - 'documented_error' => __('An error has occured.', 'wp-reset'),
296 - 'plugin_name' => __('WP Reset', 'wp-reset'),
297 - 'settings_url' => admin_url('tools.php?page=wp-reset'),
298 - 'icon_url' => $this->plugin_url . 'img/wp-reset-icon.png',
299 - 'invalid_confirmation' => __('Please type "reset" in the confirmation field.', 'wp-reset'),
300 - 'invalid_confirmation_title' => __('Invalid confirmation', 'wp-reset'),
301 - 'cancel_button' => __('Cancel', 'wp-reset'),
302 - 'ok_button' => __('OK', 'wp-reset'),
303 - 'confirm_button' => __('Reset WordPress', 'wp-reset'),
304 - 'confirm_title' => __('Are you sure you want to proceed?', 'wp-reset'),
305 - 'confirm1' => __('Clicking "Reset WordPress" will reset your site to default values. All content will be lost. There is NO UNDO.', 'wp-reset'),
306 - 'confirm2' => __('Click "Cancel" to abort.', 'wp-reset'),
307 - 'doing_reset' => __('Resetting in progress. Please wait.', 'wp-reset'),
308 - 'nonce_dismiss_notice' => wp_create_nonce('wp-reset_dismiss_notice'),
309 - 'nonce_run_tool' => wp_create_nonce('wp-reset_run_tool'),
310 - 'nonce_do_reset' => wp_create_nonce('wp-reset_do_reset'));
311 688
312 - wp_enqueue_style('wp-reset', $this->plugin_url . 'css/wp-reset.css', array(), $this->version);
313 - wp_enqueue_style('wp-reset-sweetalert2', $this->plugin_url . 'css/sweetalert2.min.css', array(), $this->version);
689 + /**
690 + * Delete all themes
691 + *
692 + * @param bool $keep_default_theme Keep default theme
693 + *
694 + * @return int Number of deleted themes.
695 + */
696 + function do_delete_themes($keep_default_theme = true)
697 + {
698 + global $wp_version;
314 699
315 - wp_enqueue_script('jquery-ui-tabs');
316 - wp_enqueue_script('wp-reset-sweetalert2', $this->plugin_url . 'js/sweetalert2.min.js', array('jquery'), $this->version, true);
317 - wp_enqueue_script('wp-reset', $this->plugin_url . 'js/wp-reset.js', array('jquery'), $this->version, true);
318 - wp_localize_script('wp-reset', 'wp_reset', $js_localize);
700 + if (!function_exists('delete_theme')) {
701 + require_once ABSPATH . 'wp-admin/includes/theme.php';
702 + }
319 703
320 - // fix for aggressive plugins that include their CSS on all pages
321 - wp_dequeue_style('uiStyleSheet');
322 - wp_dequeue_style('wpcufpnAdmin' );
323 - wp_dequeue_style('unifStyleSheet' );
324 - wp_dequeue_style('wpcufpn_codemirror');
325 - wp_dequeue_style('wpcufpn_codemirrorTheme');
326 - wp_dequeue_style('collapse-admin-css');
327 - wp_dequeue_style('jquery-ui-css');
328 - wp_dequeue_style('tribe-common-admin');
329 - wp_dequeue_style('file-manager__jquery-ui-css');
330 - wp_dequeue_style('file-manager__jquery-ui-css-theme');
331 - wp_dequeue_style('wpmegmaps-jqueryui');
332 - wp_dequeue_style('wp-botwatch-css');
333 - } // admin_enqueue_scripts
704 + if (!function_exists('request_filesystem_credentials')) {
705 + require_once ABSPATH . 'wp-admin/includes/file.php';
706 + }
334 707
708 + if (version_compare($wp_version, '5.0', '<') === true) {
709 + $default_theme = 'twentyseventeen';
710 + } else {
711 + $default_theme = 'twentytwentyone';
712 + }
335 713
336 - /**
337 - * Check if WP-CLI is available and running
338 - *
339 - * @return bool
340 - */
341 - function is_cli_running() {
342 - if (defined('WP_CLI') && WP_CLI) {
343 - return true;
344 - } else {
345 - return false;
346 - }
347 - } // is_cli_running
714 + $all_themes = wp_get_themes(array('errors' => null));
348 715
716 + if (true == $keep_default_theme) {
717 + unset($all_themes[$default_theme]);
718 + }
349 719
350 - /**
351 - * Deletes all transients.
352 - *
353 - * @return int Number of deleted transient DB entries
354 - */
355 - function do_delete_transients() {
356 - global $wpdb;
720 + foreach ($all_themes as $theme_slug => $theme_details) {
721 + $res = delete_theme($theme_slug);
722 + }
357 723
358 - $count = $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '\_transient\_%' OR option_name LIKE '\_site\_transient\_%'");
724 + if (false == $keep_default_theme) {
725 + update_option('template', '');
726 + update_option('stylesheet', '');
727 + update_option('current_theme', '');
728 + }
359 729
360 - return $count;
361 - } // do_delete_transients
730 + do_action('wp_reset_delete_themes', $all_themes);
362 731
732 + return sizeof($all_themes);
733 + } // do_delete_themes
363 734
364 - /**
365 - * Deletes all files in uploads folder.
366 - *
367 - * @return int Number of deleted files and folders.
368 - */
369 - function do_delete_uploads() {
370 - $upload_dir = wp_get_upload_dir();
371 735
372 - $this->delete_folder($upload_dir['basedir'], $upload_dir['basedir']);
736 + /**
737 + * Truncate custom tables
738 + *
739 + * @return int Number of truncated tables.
740 + */
741 + function do_truncate_custom_tables()
742 + {
743 + global $wpdb;
744 + $custom_tables = $this->get_custom_tables();
373 745
374 - return $this->delete_count;
375 - } // do_delete_uploads
746 + foreach ($custom_tables as $tbl) {
747 + $wpdb->wpreset_custom_table = $tbl['name'];
748 + // phpcs:ignore db call warning as we are using uncommon queries
749 + $wpdb->query('SET foreign_key_checks = 0'); // phpcs:ignore
750 + $wpdb->query("TRUNCATE TABLE " . $wpdb->wpreset_custom_table); // phpcs:ignore
751 + } // foreach
376 752
753 + do_action('wp_reset_truncate_custom_tables', $custom_tables);
377 754
378 - /**
379 - * Recursively deletes a folder
380 - *
381 - * @param string $folder Recursive param.
382 - * @param string $base_folder Base folder.
383 - *
384 - * @return bool
385 - */
386 - private function delete_folder($folder, $base_folder) {
387 - $files = array_diff(scandir($folder), array('.', '..'));
755 + return sizeof($custom_tables);
756 + } // do_truncate_custom_tables
388 757
389 - foreach ($files as $file) {
390 - if (is_dir($folder . DIRECTORY_SEPARATOR . $file)) {
391 - $this->delete_folder($folder . DIRECTORY_SEPARATOR . $file, $base_folder);
392 - } else {
393 - $tmp = @unlink($folder . DIRECTORY_SEPARATOR . $file);
394 - $this->delete_count = $this->delete_count + (int) $tmp;
395 - }
396 - } // foreach
397 758
398 - if ($folder != $base_folder) {
399 - $tmp = @rmdir($folder);
400 - $this->delete_count = $this->delete_count + (int) $tmp;
401 - return $tmp;
402 - } else {
403 - return true;
404 - }
405 - } // delete_folder
759 + /**
760 + * Drop custom tables
761 + *
762 + * @return int Number of dropped tables.
763 + */
764 + function do_drop_custom_tables()
765 + {
766 + global $wpdb;
767 + $custom_tables = $this->get_custom_tables();
406 768
769 + foreach ($custom_tables as $tbl) {
770 + $wpdb->wpreset_custom_table = $tbl['name'];
771 + // phpcs:ignore db call warning as we are using uncommon queries
772 + $wpdb->query('SET foreign_key_checks = 0'); // phpcs:ignore
773 + $wpdb->query("DROP TABLE IF EXISTS " . $wpdb->wpreset_custom_table); // phpcs:ignore
774 + } // foreach
407 775
408 - /**
409 - * Deactivate and delete all plugins
410 - *
411 - * @param bool $keep_wp_reset Keep WP Reset active and installed
412 - * @param bool $silent_deactivate Skip individual plugin deactivation functions when deactivating
413 - *
414 - * @return int Number of deleted plugins.
415 - */
416 - function do_delete_plugins($keep_wp_reset = true, $silent_deactivate = false) {
417 - if (!function_exists('get_plugins')) {
418 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
419 - }
776 + do_action('wp_reset_drop_custom_tables', $custom_tables);
420 777
421 - $wp_reset_basename = plugin_basename(__FILE__);
778 + return sizeof($custom_tables);
779 + } // do_drop_custom_tables
422 780
423 - $all_plugins = get_plugins();
424 - $active_plugins = (array) get_option('active_plugins', array());
425 - if (true == $keep_wp_reset) {
426 - if (($key = array_search($wp_reset_basename, $active_plugins)) !== false) {
427 - unset($active_plugins[$key]);
428 - }
429 - unset($all_plugins[$wp_reset_basename]);
430 - }
431 781
432 - if (!empty($active_plugins)) {
433 - deactivate_plugins($active_plugins, $silent_deactivate, false);
434 - }
782 + /**
783 + * Delete .htaccess file
784 + *
785 + * @return bool|WP_Error Action status.
786 + */
787 + function do_delete_htaccess()
788 + {
789 + global $wp_filesystem;
435 790
436 - if (!empty($all_plugins)) {
437 - delete_plugins(array_keys($all_plugins));
438 - }
791 + if (empty($wp_filesystem)) {
792 + require_once ABSPATH . '/wp-admin/includes/file.php';
793 + WP_Filesystem();
794 + }
439 795
440 - return sizeof($all_plugins);
441 - } // do_delete_plugins
796 + $htaccess_path = $this->get_htaccess_path();
797 + clearstatcache();
442 798
799 + do_action('wp_reset_delete_htaccess', $htaccess_path);
443 800
444 - /**
445 - * Delete all themes
446 - *
447 - * @param bool $keep_default_theme Keep default theme
448 - *
449 - * @return int Number of deleted themes.
450 - */
451 - function do_delete_themes($keep_default_theme = true) {
452 - $default_theme = 'twentyseventeen';
453 - $all_themes = wp_get_themes(array('errors' => null));
801 + if (!$wp_filesystem->is_readable($htaccess_path)) {
802 + return new WP_Error(1, 'Htaccess file does not exist; there\'s nothing to delete.');
803 + }
454 804
455 - if (true == $keep_default_theme) {
456 - unset($all_themes[$default_theme]);
457 - }
805 + if (!$wp_filesystem->is_writable($htaccess_path)) {
806 + return new WP_Error(1, 'Htaccess file is not writable.');
807 + }
458 808
459 - foreach ($all_themes as $theme_slug => $theme_details) {
460 - $res = delete_theme($theme_slug);
461 - }
809 + if ($wp_filesystem->delete($htaccess_path, false, 'f')) {
810 + return true;
811 + } else {
812 + return new WP_Error(1, 'Unknown error. Unable to delete htaccess file.');
813 + }
814 + } // do_delete_htaccess
462 815
463 - if (false == $keep_default_theme) {
464 - update_option('template', '');
465 - update_option('stylesheet', '');
466 - }
467 816
468 - return sizeof($all_themes);
469 - } // do_delete_themes
817 + /**
818 + * Get .htaccess file path.
819 + *
820 + * @return string
821 + */
822 + function get_htaccess_path()
823 + {
824 + if (!function_exists('get_home_path')) {
825 + require_once ABSPATH . 'wp-admin/includes/file.php';
826 + }
470 827
828 + if ($this->is_cli_running()) {
829 + $_SERVER['SCRIPT_FILENAME'] = ABSPATH;
830 + }
471 831
472 - /**
473 - * Truncate custom tables
474 - *
475 - * @return int Number of truncated tables.
476 - */
477 - function do_truncate_custom_tables() {
478 - global $wpdb;
479 - $custom_tables = $this->get_custom_tables();
832 + $filepath = get_home_path() . '.htaccess';
480 833
481 - foreach ($custom_tables as $tbl) {
482 - $wpdb->query('TRUNCATE TABLE ' . $tbl['name']);
483 - } // foreach
834 + return $filepath;
835 + } // get_htaccess_path
484 836
485 - return sizeof($custom_tables);
486 - } // do_truncate_custom_tables
487 837
838 + /**
839 + * Run one tool via AJAX call
840 + *
841 + * @return null
842 + */
843 + function ajax_run_tool()
844 + {
845 + check_ajax_referer('wp-reset_run_tool');
488 846
489 - /**
490 - * Drop custom tables
491 - *
492 - * @return int Number of dropped tables.
493 - */
494 - function do_drop_custom_tables() {
495 - global $wpdb;
496 - $custom_tables = $this->get_custom_tables();
847 + if (!current_user_can('manage_options')) {
848 + wp_send_json_error(__('You are not allowed to run this action.', 'wp-reset'));
849 + }
497 850
498 - foreach ($custom_tables as $tbl) {
499 - $wpdb->query('DROP TABLE IF EXISTS ' . $tbl['name']);
500 - } // foreach
851 + if(!isset($_GET['tool'])){
852 + wp_send_json_error(__('Unknown tool.', 'wp-reset'));
853 + }
501 854
502 - return sizeof($custom_tables);
503 - } // do_drop_custom_tables
855 + $tool = trim(sanitize_text_field(wp_unslash($_GET['tool'])));
504 856
857 + if(isset($_GET['extra_data'])){
858 + $extra_data = trim(sanitize_text_field(wp_unslash($_GET['extra_data'])));
859 + } else {
860 + $extra_data = '';
861 + }
505 862
506 - /**
507 - * Run one tool via AJAX call
508 - *
509 - * @return null
510 - */
511 - function ajax_run_tool() {
512 - check_ajax_referer('wp-reset_run_tool');
863 + if ($tool == 'delete_transients') {
864 + $cnt = $this->do_delete_transients();
865 + wp_send_json_success($cnt);
866 + } elseif ($tool == 'reset_theme_options') {
867 + $cnt = $this->do_reset_theme_options(true);
868 + wp_send_json_success($cnt);
869 + } elseif ($tool == 'purge_cache') {
870 + $this->do_purge_cache();
871 + wp_send_json_success();
872 + } elseif ($tool == 'delete_wp_cookies') {
873 + wp_clear_auth_cookie();
874 + wp_send_json_success();
875 + } elseif ($tool == 'delete_themes') {
876 + $cnt = $this->do_delete_themes(false);
877 + wp_send_json_success($cnt);
878 + } elseif ($tool == 'deactivate_plugins') {
879 + $cnt = $this->do_deactivate_plugins($extra_data);
880 + wp_send_json_success($cnt);
881 + } elseif ($tool == 'delete_plugins') {
882 + $cnt = $this->do_delete_plugins($extra_data);
883 + wp_send_json_success($cnt);
884 + } elseif ($tool == 'delete_uploads') {
885 + $cnt = $this->do_delete_uploads();
886 + wp_send_json_success($cnt);
887 + } elseif ($tool == 'delete_htaccess') {
888 + $tmp = $this->do_delete_htaccess();
889 + if (is_wp_error($tmp)) {
890 + wp_send_json_error($tmp->get_error_message());
891 + } else {
892 + wp_send_json_success($tmp);
893 + }
894 + } elseif ($tool == 'drop_custom_tables') {
895 + $cnt = $this->do_drop_custom_tables();
896 + wp_send_json_success($cnt);
897 + } elseif ($tool == 'truncate_custom_tables') {
898 + $cnt = $this->do_truncate_custom_tables();
899 + wp_send_json_success($cnt);
900 + } elseif ($tool == 'delete_snapshot') {
901 + $res = $this->do_delete_snapshot($extra_data);
902 + if (is_wp_error($res)) {
903 + wp_send_json_error($res->get_error_message());
904 + } else {
905 + wp_send_json_success();
906 + }
907 + } elseif ($tool == 'download_snapshot') {
908 + $res = $this->do_export_snapshot($extra_data);
909 + if (is_wp_error($res)) {
910 + wp_send_json_error($res->get_error_message());
911 + } else {
912 + $url = content_url() . '/' . $this->snapshots_folder . '/' . $res;
913 + wp_send_json_success($url);
914 + }
915 + } elseif ($tool == 'restore_snapshot') {
916 + $res = $this->do_restore_snapshot($extra_data);
917 + if (is_wp_error($res)) {
918 + wp_send_json_error($res->get_error_message());
919 + } else {
920 + wp_send_json_success();
921 + }
922 + } elseif ($tool == 'compare_snapshots') {
923 + $res = $this->do_compare_snapshots($extra_data);
924 + if (is_wp_error($res)) {
925 + wp_send_json_error($res->get_error_message());
926 + } else {
927 + wp_send_json_success($res);
928 + }
929 + } elseif ($tool == 'create_snapshot') {
930 + $res = $this->do_create_snapshot($extra_data);
931 + if (is_wp_error($res)) {
932 + wp_send_json_error($res->get_error_message());
933 + } else {
934 + wp_send_json_success();
935 + }
936 + } elseif ($tool == 'get_table_details') {
937 + $res = WP_Reset_Utility::get_table_details();
938 + wp_send_json_success($res);
939 + } elseif (
940 + $tool == 'check_deactivate_plugin' ||
941 + $tool == 'check_delete_plugin' ||
942 + $tool == 'check_install_plugin' ||
943 + $tool == 'check_activate_plugin'
944 + ) {
945 + if(!isset($_GET['slug'])){
946 + wp_send_json_success('unknown plugin');
947 + }
948 +
949 + $slug = sanitize_text_field(wp_unslash($_GET['slug']));
950 + $path = $this->get_plugin_path($slug);
513 951
514 - $tool = trim(@$_GET['tool']);
515 - $extra_data = trim(@$_GET['extra_data']);
952 + if (false !== ($error = get_transient('wf_install_error_' . $slug))) {
953 + delete_transient('wf_install_error_' . $slug);
954 + wp_send_json_success($error);
955 + }
516 956
517 - if ($tool == 'delete_transients') {
518 - $cnt = $this->do_delete_transients();
519 - wp_send_json_success($cnt);
520 - } elseif ($tool == 'delete_themes') {
521 - $cnt = $this->do_delete_themes(false);
522 - wp_send_json_success($cnt);
523 - } elseif ($tool == 'delete_plugins') {
524 - $cnt = $this->do_delete_plugins(true);
525 - wp_send_json_success($cnt);
526 - } elseif ($tool == 'delete_uploads') {
527 - $cnt = $this->do_delete_uploads();
528 - wp_send_json_success($cnt);
529 - } elseif ($tool == 'drop_custom_tables') {
530 - $cnt = $this->do_drop_custom_tables();
531 - wp_send_json_success($cnt);
532 - } elseif ($tool == 'truncate_custom_tables') {
533 - $cnt = $this->do_truncate_custom_tables();
534 - wp_send_json_success($cnt);
535 - } elseif ($tool == 'delete_snapshot') {
536 - $res = $this->do_delete_snapshot($extra_data);
537 - if (is_wp_error($res)) {
538 - wp_send_json_error($res->get_error_message());
539 - } else {
540 - wp_send_json_success();
541 - }
542 - } elseif ($tool == 'download_snapshot') {
543 - $res = $this->do_export_snapshot($extra_data);
544 - if (is_wp_error($res)) {
545 - wp_send_json_error($res->get_error_message());
546 - } else {
547 - $url = content_url() . '/' . $this->snapshots_folder . '/' . $res;
548 - wp_send_json_success($url);
549 - }
550 - } elseif ($tool == 'restore_snapshot') {
551 - $res = $this->do_restore_snapshot($extra_data);
552 - if (is_wp_error($res)) {
553 - wp_send_json_error($res->get_error_message());
554 - } else {
555 - wp_send_json_success();
556 - }
557 - } elseif ($tool == 'compare_snapshots') {
558 - $res = $this->do_compare_snapshots($extra_data);
559 - if (is_wp_error($res)) {
560 - wp_send_json_error($res->get_error_message());
561 - } else {
562 - wp_send_json_success($res);
563 - }
564 - } elseif ($tool == 'create_snapshot') {
565 - $res = $this->do_create_snapshot($extra_data);
566 - if (is_wp_error($res)) {
567 - wp_send_json_error($res->get_error_message());
568 - } else {
569 - wp_send_json_success();
570 - }
571 - } else {
572 - wp_send_json_error(__('Unknown tool.', 'wp-reset'));
573 - }
574 - } // ajax_run_tool
957 + if (false !== $path) {
958 + $active_plugins = (array) get_option('active_plugins', array());
959 + if (false !== array_search($path, $active_plugins)) {
960 + wp_send_json_success('active');
961 + } else {
962 + wp_send_json_success('inactive');
963 + }
964 + } else {
965 + wp_send_json_success('deleted');
966 + }
967 + } elseif ($tool == 'install_plugin') {
968 + if(!isset($_GET['slug'])){
969 + wp_send_json_success('unknown plugin');
970 + }
971 +
972 + $slug = sanitize_text_field(wp_unslash($_GET['slug']));
575 973
974 + @include_once ABSPATH . 'wp-admin/includes/plugin.php';
975 + @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
976 + @include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
977 + @include_once ABSPATH . 'wp-admin/includes/file.php';
978 + @include_once ABSPATH . 'wp-admin/includes/misc.php';
576 979
577 - /**
578 - * Reinstall / reset the WP site
579 - * There are no failsafes in the function - it reinstalls when called
580 - * Redirects when done
581 - *
582 - * @param array $params Optional.
583 - *
584 - * @return null
585 - */
586 - function do_reinstall($params = array()) {
587 - global $current_user, $wpdb;
980 + wp_cache_flush();
588 981
589 - // only admins can reset; double-check
590 - if (!$this->is_cli_running() && !current_user_can('administrator')) {
591 - return false;
592 - }
982 + $path = $this->get_plugin_path($slug);
593 983
594 - // make sure the function is available to us
595 - if (!function_exists('wp_install')) {
596 - require ABSPATH . '/wp-admin/includes/upgrade.php';
597 - }
984 + if (false !== $path) {
985 + // Plugin is already installed
986 + wp_send_json_success();
987 + } else {
988 + // Install Plugin
989 + $skin = new WP_Ajax_Upgrader_Skin();
990 + $upgrader = new Plugin_Upgrader($skin);
991 + $upgrader->install('https://downloads.wordpress.org/plugin/' . $slug . '.latest-stable.zip');
992 + wp_send_json_success();
993 + }
994 + } elseif ($tool == 'activate_plugin') {
995 + if(!isset($_GET['slug'])){
996 + wp_send_json_success('unknown plugin');
997 + }
998 +
999 + $slug = sanitize_text_field(wp_unslash($_GET['slug']));
598 1000
599 - // save values that need to be restored after reset
600 - // todo: use params to determine what gets restored after reset
601 - $blogname = get_option('blogname');
602 - $blog_public = get_option('blog_public');
603 - $wplang = get_option('wplang');
604 - $siteurl = get_option('siteurl');
605 - $home = get_option('home');
606 - $snapshots = $this->get_snapshots();
1001 + $path = $this->get_plugin_path($slug);
1002 + activate_plugin($path);
1003 + wp_send_json_success();
1004 + } elseif ($tool == 'before_reset') {
1005 + $active_plugins = get_option('active_plugins');
1006 + set_transient('wpr_active_plugins', $active_plugins, 100);
1007 + remove_all_actions('update_option_active_plugins');
1008 + update_option('active_plugins', array(plugin_basename(__FILE__)));
1009 + wp_send_json_success();
1010 + } else {
1011 + wp_send_json_error(__('Unknown tool.', 'wp-reset'));
1012 + }
1013 + } // ajax_run_tool
607 1014
608 - $active_plugins = get_option('active_plugins');
609 - $active_theme = wp_get_theme();
610 1015
611 - // for WP-CLI
612 - if (!$current_user->ID) {
613 - $tmp = get_users(array('role' => 'administrator', 'order' => 'ASC', 'order_by' => 'ID'));
614 - if (empty($tmp[0]->user_login)) {
615 - return new WP_Error('no_user', 'Reset failed. Unable to find any admin users in database.');
616 - }
617 - $current_user = $tmp[0];
618 - }
1016 + /**
1017 + * Get plugin path from slug
1018 + *
1019 + * @return string path
1020 + */
1021 + function get_plugin_path($slug)
1022 + {
1023 + $all_plugins = get_plugins();
1024 + foreach ($all_plugins as $plugin_path => $plugin) {
1025 + if (strpos($plugin_path, $slug . '/') === 0) {
1026 + return $plugin_path;
1027 + }
1028 + }
1029 + return false;
1030 + } // get_plugin_path
619 1031
620 - // delete custom tables with WP's prefix
621 - $prefix = str_replace('_', '\_', $wpdb->prefix);
622 - $tables = $wpdb->get_col("SHOW TABLES LIKE '{$prefix}%'");
623 - foreach ($tables as $table) {
624 - $wpdb->query("DROP TABLE $table");
625 - }
626 1032
627 - // supress errors for WP_CLI
628 - // todo: do something better
629 - $result = @wp_install($blogname, $current_user->user_login, $current_user->user_email, $blog_public, '', md5(rand()), $wplang);
630 - $user_id = $result['user_id'];
1033 + /**
1034 + * Reinstall / reset the WP site
1035 + * There are no failsafes in the function - it reinstalls when called
1036 + * Redirects when done
1037 + *
1038 + * @param array $params Optional.
1039 + *
1040 + * @return null
1041 + */
1042 + function do_reinstall($params = array())
1043 + {
1044 + global $current_user, $wpdb;
631 1045
632 - // restore user pass
633 - $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));
634 - $wpdb->query($query);
1046 + // only admins can reset; double-check
1047 + if (!$this->is_cli_running() && !current_user_can('manage_options')) {
1048 + return false;
1049 + }
635 1050
636 - // restore rest of the settings including WP Reset's
637 - update_option('siteurl', $siteurl);
638 - update_option('home', $home);
639 - update_option('wp-reset', $this->options);
640 - update_option('wp-reset-snapshots', $snapshots);
1051 + // make sure the function is available to us
1052 + if (!function_exists('wp_install')) {
1053 + require ABSPATH . '/wp-admin/includes/upgrade.php';
1054 + }
641 1055
642 - // remove password nag
643 - if (get_user_meta($user_id, 'default_password_nag')) {
644 - update_user_meta($user_id, 'default_password_nag', false);
645 - }
646 - if (get_user_meta($user_id, $wpdb->prefix . 'default_password_nag')) {
647 - update_user_meta($user_id, $wpdb->prefix . 'default_password_nag', false );
648 - }
1056 + // save values that need to be restored after reset
1057 + $blogname = get_option('blogname');
1058 + $blog_public = get_option('blog_public');
1059 + $wplang = get_option('wplang');
1060 + $siteurl = get_option('siteurl');
1061 + $home = get_option('home');
1062 + $snapshots = $this->get_snapshots();
649 1063
650 - $meta = $this->get_meta();
651 - $meta['reset_count']++;
652 - $this->update_options('meta', $meta);
1064 + $active_plugins = get_transient('wpr_active_plugins');
1065 + $active_theme = wp_get_theme();
653 1066
654 - // reactivate theme
655 - if (!empty($params['reactivate_theme'])) {
656 - switch_theme($active_theme->get_stylesheet());
657 - }
1067 + // for WP-CLI
1068 + if (!$current_user->ID) {
1069 + $tmp = get_users(array('role' => 'administrator', 'order' => 'ASC', 'order_by' => 'ID'));
1070 + if (empty($tmp[0]->user_login)) {
1071 + return new WP_Error(1, 'Reset failed. Unable to find any admin users in database.');
1072 + }
1073 + $current_user = $tmp[0];
1074 + }
658 1075
659 - // reactivate WP Reset
660 - if (!empty($params['reactivate_wpreset'])) {
661 - activate_plugin(plugin_basename( __FILE__ ));
662 - }
1076 + // delete custom tables with WP's prefix
1077 + $prefix = str_replace('_', '\_', $wpdb->prefix);
663 1078
664 - // reactivate all plugins
665 - if (!empty($params['reactivate_plugins'])) {
666 - foreach ($active_plugins as $plugin_file) {
667 - activate_plugin($plugin_file);
668 - }
669 - }
1079 + // phpcs:ignore db call warning as we are using uncommon queries
1080 + $tables = $wpdb->get_col($wpdb->prepare("SHOW TABLES LIKE %s", array($prefix . '%'))); // phpcs:ignore
670 1081
671 - if (!$this->is_cli_running()) {
672 - // log out and log in the old/new user
673 - // since the password doesn't change this is potentially unnecessary
674 - wp_clear_auth_cookie();
675 - wp_set_auth_cookie($user_id);
1082 + foreach ($tables as $table) {
1083 + $wpdb->wpreset_table = $table;
1084 + $wpdb->query("DROP TABLE " . $wpdb->wpreset_table); // phpcs:ignore
1085 + }
676 1086
677 - wp_redirect(admin_url() . '?wp-reset=success');
678 - exit;
679 - }
680 - } // do_reinstall
1087 + $old_user_pass = $current_user->user_pass;
681 1088
1089 + // suppress errors for WP_CLI
1090 + $result = @wp_install($blogname, $current_user->user_login, $current_user->user_email, $blog_public, '', md5(wp_rand()), $wplang);
1091 + $user_id = $result['user_id'];
682 1092
683 - /**
684 - * Checks wp_reset post value and performs all actions
685 - * todo: handle messages for various actions
686 - *
687 - * @return null|bool
688 - */
689 - function do_all_actions() {
690 - // only admins can perform actions
691 - if (!current_user_can('administrator')) {
692 - return;
693 - }
1093 + // restore user pass
1094 + // phpcs:ignore db call warning as we are using uncommon queries
1095 + $wpdb->query($wpdb->prepare("UPDATE {$wpdb->users} SET user_pass = %s, user_activation_key = %s WHERE ID = %d LIMIT 1", array($old_user_pass, '', $user_id))); // phpcs:ignore
1096 + $current_user->user_pass = $old_user_pass;
694 1097
695 - if (!empty($_GET['wp-reset']) && stristr($_SERVER['HTTP_REFERER'], 'wp-reset')) {
696 - add_action('admin_notices', array($this, 'notice_successfull_reset'));
697 - }
1098 + // restore rest of the settings including WP Reset's
1099 + update_option('siteurl', $siteurl);
1100 + update_option('home', $home);
1101 + update_option('wp-reset', $this->options);
1102 + update_option('wp-reset-snapshots', $snapshots);
698 1103
699 - // check nonce
700 - if (true === isset($_POST['wp_reset_confirm']) && false === wp_verify_nonce(@$_POST['_wpnonce'], 'wp-reset')) {
701 - add_settings_error('wp-reset', 'bad-nonce', __('Something went wrong. Please refresh the page and try again.', 'wp-reset'), 'error');
702 - return false;
703 - }
1104 + // remove password nag
1105 + if (get_user_meta($user_id, 'default_password_nag')) {
1106 + update_user_meta($user_id, 'default_password_nag', false);
1107 + }
1108 + if (get_user_meta($user_id, $wpdb->prefix . 'default_password_nag')) {
1109 + update_user_meta($user_id, $wpdb->prefix . 'default_password_nag', false);
1110 + }
704 1111
705 - // check confirmation code
706 - if (true === isset($_POST['wp_reset_confirm']) && 'reset' !== $_POST['wp_reset_confirm']) {
707 - add_settings_error('wp-reset', 'bad-confirm', __('<b>Invalid confirmation code.</b> Please type "reset" in the confirmation field.', 'wp-reset'), 'error');
708 - return false;
709 - }
1112 + $meta = $this->get_meta();
1113 + $meta['reset_count']++;
1114 + $this->update_options('meta', $meta);
710 1115
711 - // only one action at the moment
712 - if (true === isset($_POST['wp_reset_confirm']) && 'reset' === $_POST['wp_reset_confirm']) {
713 - $defaults = array('reactivate_theme' => '0',
714 - 'reactivate_plugins' => '0',
715 - 'reactivate_wpreset' => '0');
716 - $params = shortcode_atts($defaults, (array) @$_POST['wpr-post-reset']);
1116 + // reactivate theme
1117 + if (!empty($params['reactivate_theme'])) {
1118 + switch_theme($active_theme->get_stylesheet());
1119 + }
717 1120
718 - $this->do_reinstall($params);
719 - }
720 - } // do_all_actions
1121 + // reactivate WP Reset
1122 + if (!empty($params['reactivate_wpreset'])) {
1123 + activate_plugin(plugin_basename(__FILE__));
1124 + }
721 1125
1126 + // reactivate all plugins
1127 + if (!empty($params['reactivate_plugins'])) {
1128 + foreach ($active_plugins as $plugin_file) {
1129 + activate_plugin($plugin_file);
1130 + }
1131 + }
722 1132
723 - /**
724 - * Add "Open WP Reset Tools" action link to plugins table, left part
725 - *
726 - * @param array $links Initial list of links.
727 - *
728 - * @return array
729 - */
730 - function plugin_action_links($links) {
731 - $settings_link = '<a href="' . admin_url('tools.php?page=wp-reset') . '" title="' . __('Open WP Reset Tools', 'wp-reset') . '">' . __('Open WP Reset Tools', 'wp-reset') . '</a>';
1133 + if (!$this->is_cli_running()) {
1134 + // log out and log in the old/new user
1135 + // since the password doesn't change this is potentially unnecessary
1136 + wp_clear_auth_cookie();
1137 + wp_set_auth_cookie($user_id);
732 1138
733 - array_unshift($links, $settings_link);
1139 + wp_safe_redirect(admin_url() . '?wp-reset=success');
1140 + exit;
1141 + }
1142 + } // do_reinstall
734 1143
735 - return $links;
736 - } // plugin_action_links
737 1144
1145 + /**
1146 + * Checks wp_reset post value and performs all actions
1147 + *
1148 + * @return null|bool
1149 + */
1150 + function do_all_actions()
1151 + {
1152 + // only admins can perform actions
1153 + if (!current_user_can('manage_options')) {
1154 + return;
1155 + }
738 1156
739 - /**
740 - * Add links to plugin's description in plugins table
741 - *
742 - * @param array $links Initial list of links.
743 - * @param string $file Basename of current plugin.
744 - *
745 - * @return array
746 - */
747 - function plugin_meta_links($links, $file) {
748 - if ($file !== plugin_basename(__FILE__)) {
749 - return $links;
750 - }
1157 + if (!empty($_GET['wp-reset']) && sanitize_text_field(wp_unslash($_GET['wp-reset'])) == 'success') {
1158 + add_action('admin_notices', array($this, 'notice_successful_reset'));
1159 + }
751 1160
752 - $support_link = '<a target="_blank" href="https://wordpress.org/support/plugin/wp-reset" title="' . __('Get help', 'wp-reset') . '">' . __('Support', 'wp-reset') . '</a>';
753 - $home_link = '<a target="_blank" href="' . $this->generate_web_link('plugins-table-right') . '" title="' . __('Plugin Homepage', 'wp-reset') . '">' . __('Plugin Homepage', 'wp-reset') . '</a>';
754 - $rate_link = '<a target="_blank" href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post" title="' . __('Rate the plugin', 'wp-reset') . '">' . __('Rate the plugin ★★★★★', 'wp-reset') . '</a>';
1161 + // check nonce
1162 + if (true === isset($_POST['wp_reset_confirm']) && (!isset($_POST['_wpnonce']) || false === wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'wp-reset'))) {
1163 + add_settings_error('wp-reset', 'bad-nonce', __('Something went wrong. Please refresh the page and try again.', 'wp-reset'), 'error');
1164 + return false;
1165 + }
755 1166
756 - $links[] = $support_link;
757 - $links[] = $home_link;
758 - $links[] = $rate_link;
1167 + // check confirmation code
1168 + if (true === isset($_POST['wp_reset_confirm']) && 'reset' !== sanitize_text_field(wp_unslash($_POST['wp_reset_confirm']))) {
1169 + add_settings_error('wp-reset', 'bad-confirm', __('<b>Invalid confirmation code.</b> Please type "reset" in the confirmation field.', 'wp-reset'), 'error');
1170 + return false;
1171 + }
759 1172
760 - return $links;
761 - } // plugin_meta_links
1173 + // only one action at the moment
1174 + if (true === isset($_POST['wp_reset_confirm']) && 'reset' === sanitize_text_field(wp_unslash($_POST['wp_reset_confirm']))) {
1175 + $params = array(
1176 + 'reactivate_theme' => '0',
1177 + 'reactivate_plugins' => '0',
1178 + 'reactivate_wpreset' => '0',
1179 + );
1180 + if (isset($_POST['wpr-post-reset']['reactivate_theme'])) {
1181 + $params['reactivate_theme'] = true;
1182 + }
1183 + if (isset($_POST['wpr-post-reset']['reactivate_plugins'])) {
1184 + $params['reactivate_plugins'] = true;
1185 + }
1186 + if (isset($_POST['wpr-post-reset']['reactivate_wpreset'])) {
1187 + $params['reactivate_wpreset'] = true;
1188 + }
762 1189
1190 + $this->do_reinstall($params);
1191 + }
1192 + } // do_all_actions
763 1193
764 - /**
765 - * Test if we're on WPR's admin page
766 - *
767 - * @return bool
768 - */
769 - function is_plugin_page() {
770 - $current_screen = get_current_screen();
771 1194
772 - if ($current_screen->id == 'tools_page_wp-reset') {
773 - return true;
774 - } else {
775 - return false;
776 - }
777 - } // is_plugin_page
1195 + /**
1196 + * Add "Open WP Reset Tools" action link to plugins table, left part
1197 + *
1198 + * @param array $links Initial list of links.
1199 + *
1200 + * @return array
1201 + */
1202 + function plugin_action_links($links)
1203 + {
1204 + $settings_link = '<a href="' . esc_url(admin_url('tools.php?page=wp-reset')) . '" title="' . esc_attr(__('Open WP Reset Tools', 'wp-reset')) . '">' . esc_html(__('Open WP Reset Tools', 'wp-reset')) . '</a>';
778 1205
1206 + array_unshift($links, $settings_link);
779 1207
780 - /**
781 - * Add powered by text in admin footer
782 - *
783 - * @param string $text Default footer text.
784 - *
785 - * @return string
786 - */
787 - function admin_footer_text($text) {
788 - if (!$this->is_plugin_page()) {
789 - return $text;
790 - }
1208 + return $links;
1209 + } // plugin_action_links
791 1210
792 - $text = '<i><a href="' . $this->generate_web_link('admin_footer') . '" title="' . __('Visit WP Reset page for more info', 'wp-reset') . '" target="_blank">WP Reset</a> v' . $this->version . ' by <a href="https://www.webfactoryltd.com/" title="' . __('Visit our site to get more great plugins', 'wp-reset'). '" target="_blank">WebFactory Ltd</a>. Proudly sponsored by <a target="_blank" href="https://ipgeolocation.io/">IP Geolocation</a> - premium GeoIP service for developers.</i>';
793 1211
794 - return $text;
795 - } // admin_footer_text
1212 + /**
1213 + * Add links to plugin's description in plugins table
1214 + *
1215 + * @param array $links Initial list of links.
1216 + * @param string $file Basename of current plugin.
1217 + *
1218 + * @return array
1219 + */
1220 + function plugin_meta_links($links, $file)
1221 + {
1222 + if ($file !== plugin_basename(__FILE__)) {
1223 + return $links;
1224 + }
796 1225
1226 + $support_link = '<a target="_blank" href="https://wordpress.org/support/plugin/wp-reset" title="' . __('Get help', 'wp-reset') . '">' . __('Support', 'wp-reset') . '</a>';
1227 + $home_link = '<a target="_blank" href="' . esc_url($this->generate_web_link('plugins-table-right')) . '" title="' . __('Plugin Homepage', 'wp-reset') . '">' . __('Plugin Homepage', 'wp-reset') . '</a>';
1228 + $rate_link = '<a target="_blank" href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post" title="' . __('Rate the plugin', 'wp-reset') . '">' . __('Rate the plugin ★★★★★', 'wp-reset') . '</a>';
797 1229
798 - /**
799 - * Loads plugin's translated strings
800 - *
801 - * @return null
802 - */
803 - function load_textdomain() {
804 - load_plugin_textdomain('wp-reset');
805 - } // load_textdomain
1230 + $links[] = $support_link;
1231 + $links[] = $home_link;
1232 + $links[] = $rate_link;
806 1233
1234 + return $links;
1235 + } // plugin_meta_links
807 1236
808 - /**
809 - * Inform the user that WordPress has been successfully reset
810 - *
811 - * @return null
812 - */
813 - function notice_successfull_reset() {
814 - global $current_user;
815 1237
816 - echo '<div id="message" class="updated fade"><p>' . sprintf(__( '<b>Site has been reset</b> to default settings. User "%s" was restored with the password unchanged. Open <a href="%s">WP Reset</a> to do another reset.', 'wp-reset'), $current_user->user_login, admin_url('tools.php?page=wp-reset')) . '</p></div>';
817 - } // notice_successfull_reset
1238 + /**
1239 + * Test if we're on WPR's admin page
1240 + *
1241 + * @return bool
1242 + */
1243 + function is_plugin_page()
1244 + {
1245 + $current_screen = get_current_screen();
818 1246
1247 + if (!empty($current_screen->id) && $current_screen->id == 'tools_page_wp-reset') {
1248 + return true;
1249 + } else {
1250 + return false;
1251 + }
1252 + } // is_plugin_page
819 1253
820 - /**
821 - * Outputs complete plugin's admin page
822 - *
823 - * @return null
824 - */
825 - function plugin_page() {
826 - $notice_shown = false;
827 - $meta = $this->get_meta();
828 - $notices = $this->get_dismissed_notices();
829 - $snapshots = $this->get_snapshots();
830 1254
831 - // double check for admin priv
832 - if (!current_user_can('administrator')) {
833 - wp_die(__('Sorry, you are not allowed to access this page.', 'wp-reset'));
834 - }
1255 + /**
1256 + * Add powered by text in admin footer
1257 + *
1258 + * @param string $text Default footer text.
1259 + *
1260 + * @return string
1261 + */
1262 + function admin_footer_text($text)
1263 + {
1264 + if (!$this->is_plugin_page()) {
1265 + return $text;
1266 + }
835 1267
836 - settings_errors();
837 - echo '<div class="wrap">';
838 - echo '<h1><img id="logo-icon" src="' . $this->plugin_url . 'img/wp-reset-logo.png" title="' . __('WP Reset', 'wp-reset') . '" alt="' . __('WP Reset', 'wp-reset') . '"> <span>proudly sponsored by <a href="https://ipgeolocation.io/" target="_blank">IP Geolocation</a></span></h1>';
839 - echo '<form id="wp_reset_form" action="' . admin_url('tools.php?page=wp-reset') . '" method="post" autocomplete="off">';
1268 + $text = '<i><a href="' . esc_url($this->generate_web_link('admin_footer')) . '" title="' . esc_attr(__('Visit WP Reset page for more info', 'wp-reset')) . '" target="_blank">WP Reset</a> v' . $this->version . '. Please <a target="_blank" href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post" title="Rate the plugin">rate the plugin <span>★★★★★</span></a> to help us spread the word. Thank you from the WP Reset team!</i>';
840 1269
841 - if (false === $notice_shown && is_multisite()) {
842 - echo '<div class="card notice-wrapper notice-error">';
843 - echo '<h2>' . __('WP Reset is not compatible with multisite!', 'wp-reset') . '</h2>';
844 - echo '<p>' . __('Please be careful when using WP Reset with multisite enabled. It\'s not recommended to reset the main site. Sub-sites should be OK. We\'re working on making it fully compatible with WP-MU. <b>Till then please be careful.</b> Thank you for understanding.', 'wp-reset') . '</p>';
845 - echo '</div>';
846 - $notice_shown = true;
847 - }
1270 + return $text;
1271 + } // admin_footer_text
848 1272
849 - if ((!empty($meta['reset_count']) || !empty($snapshots)) && false === $notice_shown && false == $this->get_dismissed_notices('rate')) {
850 - echo '<div class="card notice-wrapper">';
851 - echo '<h2>' . __('Please help us keep the plugin free &amp; up-to-date', 'wp-reset') . '</h2>';
852 - echo '<p>' . __('If you use &amp; enjoy WP Reset, <b>please rate it on WordPress.org</b>. It only takes a second and helps us keep the plugin free and maintained. Thank you!', 'wp-reset') . '</p>';
853 - echo '<p><a class="button-primary button" title="' . __('Rate WP Reset', 'wp-reset') . '" target="_blank" href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post">' . __('Help keep the plugin free - rate it!', 'wp-reset') . '</a> <a href="#" class="wpr-dismiss-notice dismiss-notice-rate" data-notice="rate">' . __('I\'ve already rated it', 'wp-reset') . '</a></p>';
854 - echo '</div>';
855 - $notice_shown = true;
856 - }
857 1273
858 - // Tidy Repo ad
859 - // disabled for now
860 - if (false && false === $notice_shown && $meta['reset_count'] >= 2 && false == $this->get_dismissed_notices('tidy')) {
861 - echo '<div class="card notice-wrapper">';
862 - echo '<h2>' . __('Are you a plugin author? Get your plugin reviewed on Tidy Repo', 'wp-reset') . '</h2>';
863 - echo '<p>' . __('Since 2013 Tidy Repo has been reviewing the best and most reliable WordPress plugins. <b>Submitting a plugin is free</b>, so you have nothing to lose and a lot of exposure to gain when it gets reviewed.', 'wp-reset') . '</p>';
864 - echo '<p><a class="button-primary button" title="' . __('Rate WP Reset', 'wp-reset') . '" target="_blank" href="https://tidyrepo.com/?utm_source=wp-reset-free&utm_medium=plugin&utm_content=notification&utm_campaign=wp-reset-free-v' . $this->version . '">' . __('Let Tidy Repo know you have a great plugin', 'wp-reset') . '</a> <a href="#" class="wpr-dismiss-notice dismiss-notice-rate" data-notice="tidy">' . __('Thanks, I\'m not interested', 'wp-reset') . '</a></p>';
865 - echo '</div>';
866 - $notice_shown = true;
867 - }
1274 + /**
1275 + * Loads plugin's translated strings
1276 + *
1277 + * @return null
1278 + */
1279 + function load_textdomain()
1280 + {
1281 + load_plugin_textdomain('wp-reset');
1282 + } // load_textdomain
868 1283
869 - // tabs
870 - echo '<div id="wp-reset-tabs" class' . __('="', 'wp-reset') . 'ui-tabs">';
871 1284
872 - echo '<ul class="wpr-main-tab">';
873 - echo '<li><a href="#tab-reset">' . __('Reset', 'wp-reset') . '</a></li>';
874 - echo '<li><a href="#tab-tools">' . __('Tools', 'wp-reset') . '</a></li>';
875 - echo '<li><a href="#tab-snapshots">' . __('DB Snapshots', 'wp-reset') . '</a></li>';
876 - echo '<li><a href="#tab-support">' . __('Support', 'wp-reset') . '</a></li>';
877 - if (empty($notices['geoip_tab'])) {
878 - echo '<li><a href="#tab-geoip">' . __('IP Geolocation', 'wp-reset') . '</a></li>';
879 - }
880 - echo '</ul>';
1285 + /**
1286 + * Inform the user that WordPress has been successfully reset
1287 + *
1288 + * @return null
1289 + */
1290 + function notice_successful_reset()
1291 + {
1292 + global $current_user;
1293 + /* translators: %1$s is replaced with the current user's username, %2$s is replaced with the URL to WP Reset settings page */
1294 + WP_Reset_Utility::wp_kses_wf('<div style="padding: 15px; display: inline-block; font-size: 14px;" id="message" class="updated"><p style="font-size: 14px;">' . sprintf(__('<b>Site has been successfully reset to default settings.</b><br>User "%1$s" was restored with the password unchanged. Open <a href="%2$s">WP Reset</a> to do another reset.', 'wp-reset'), esc_html($current_user->user_login), esc_url(admin_url('tools.php?page=wp-reset'))) . '</p>');
881 1295
882 - echo '<div style="display: none;" id="tab-reset">';
883 - $this->tab_reset();
884 - echo '</div>';
885 1296
886 - echo '<div style="display: none;" id="tab-tools">';
887 - $this->tab_tools();
888 - echo '</div>';
1297 + if (false == $this->get_dismissed_notices('rate')) {
1298 + $request_url = isset($_SERVER['REQUEST_URI'])?sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])):'';
1299 + $dismiss_url = add_query_arg(array('action' => 'wpr_dismiss_notice', 'notice' => 'rate', 'redirect' => urlencode($request_url)), admin_url('admin.php'));
1300 + $dismiss_url = wp_nonce_url($dismiss_url, 'wpr_dismiss_notice');
889 1301
890 - echo '<div style="display: none;" id="tab-snapshots">';
891 - $this->tab_snapshots();
892 - echo '</div>';
1302 + echo '<p style="font-size: 14px;">';
1303 + echo 'If WP Reset helped you please rate it so we can continue supporting it and helping others. Thank you!<br>';
1304 + echo '<a style="margin-top: 5px;" class="button button-secondary" href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post" target="_blank">You deserve it, I\'ll rate it!</a> &nbsp; &nbsp; <a href="' . esc_url($dismiss_url) . '">I already rated it</a>';
1305 + echo '</p>';
1306 + }
893 1307
894 - echo '<div style="display: none;" id="tab-support">';
895 - $this->tab_support();
896 - echo '</div>';
1308 + echo '</div>';
1309 + } // notice_successful_reset
897 1310
898 - if (empty($notices['geoip_tab'])) {
899 - echo '<div style="display: none;" id="tab-geoip">';
900 - $this->tab_geoip();
901 - echo '</div>';
902 - }
903 1311
904 - echo '</div>'; // tabs
1312 + /**
1313 + * Generate a button that initiates snapshot creation
1314 + *
1315 + * @param string $tool_id Tool ID.
1316 + * @param string $description Snapshot description.
1317 + *
1318 + * @return string
1319 + */
1320 + function get_snapshot_button($tool_id = '', $description = '')
1321 + {
1322 + $out = '';
1323 + $out .= '<a data-tool-id="' . esc_attr($tool_id) . '" data-description="' . esc_attr($description) . '" class="button create-new-snapshot" href="#">Create snapshot</a>';
905 1324
906 - echo '</form>';
907 - echo '</div>'; // wrap
908 - } // plugin_page
1325 + return $out;
1326 + } // get_snapshot_button
909 1327
910 1328
911 - /**
912 - * Echoes content for reset tab
913 - *
914 - * @return null
915 - */
916 - private function tab_reset() {
917 - global $current_user, $wpdb;
1329 + /**
1330 + * Generate card header including title and action buttons
1331 + *
1332 + * @param string $title Card title.
1333 + * @param string $card_id Card element #ID.
1334 + * @param array $params Individual icons arguments
1335 + *
1336 + * @return string
1337 + */
1338 + function get_card_header($title, $card_id, $params = array())
1339 + {
1340 + $params = shortcode_atts(array(
1341 + 'documentation_link' => false,
1342 + 'iot_button' => false,
1343 + 'collapse_button' => false,
1344 + 'create_snapshot' => false,
1345 + 'pro' => false
1346 + ), (array) $params);
918 1347
919 - echo '<div class="card" id="card-description">';
920 - echo '<a class="toggle-card" href="#" title="' . __('Collapse / expand box', 'wp-reset') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a>';
921 - echo '<h2>' . __('Please read carefully before proceeding. There is NO UNDO!', 'wp-reset') . '</h2>';
922 - echo '<b class="red">' . __('Resetting will delete:', 'wp-reset') . '</b>';
923 - echo '<ul class="plain-list">';
924 - echo '<li>' . __('all posts, pages, custom post types, comments, media entries, users', 'wp-reset') . '</li>';
925 - echo '<li>' . __('all default WP database tables', 'wp-reset') . '</li>';
926 - echo '<li>' . sprintf(__('all custom database tables that have the same prefix "%s" as default tables in this installation', 'wp-reset'), $wpdb->prefix) . '</li>';
927 - echo '</ul>';
1348 + if ($params['documentation_link'] === true) {
1349 + $params['documentation_link'] = $card_id;
1350 + }
928 1351
929 - echo '<b class="green">' . __('Resetting will not delete:', 'wp-reset') . '</b>';
930 - echo '<ul class="plain-list">';
931 - echo '<li>' . __('media files - they\'ll remain in the <i>wp-uploads</i> folder but will no longer be listed under Media', 'wp-reset') . '</li>';
932 - echo '<li>' . __('no files are touched; plugins, themes, uploads - everything stays', 'wp-reset') . '</li>';
933 - echo '<li>' . __('site title, WordPress address, site address, site language and search engine visibility settings', 'wp-reset') . '</li>';
934 - echo '<li>' . sprintf(__('logged in user "%s" will be restored with the current password', 'wp-reset'), $current_user->user_login) . '</li>';
935 - echo '</ul>';
1352 + $out = '';
1353 + $out .= '<h4 id="' . esc_attr($card_id) . '"><span class="card-name">' . esc_html($title);
1354 + if ($params['pro']) {
1355 + $out .= ' - <a data-feature="' . esc_attr($card_id) . '" class="pro-feature tooltip" title="WP Reset PRO tool" href="#"><span class="pro">PRO</span> tool</a>';
1356 + }
1357 + $out .= '</span>';
1358 + $out .= '<div class="card-header-right">';
1359 + if ($params['documentation_link']) {
1360 + $out .= '<a class="documentation-link tooltip" href="' . esc_url($this->generate_web_link('documentation_link', '/documentation/')) . '" title="' . __('Open documentation for this tool', 'wp-reset') . '" target="blank"><span class="dashicons dashicons-editor-help"></span></a>';
1361 + }
1362 + if ($params['iot_button']) {
1363 + $out .= '<a class="scrollto tooltip" href="#iot" title="Jump to Index of Tools"><span class="dashicons dashicons-screenoptions"></span></a>';
1364 + }
1365 + if ($params['create_snapshot']) {
1366 + $out .= '<a id="create-new-snapshot-primary" title="Create a new snapshot" href="#" class="button button-primary create-new-snapshot tooltip">' . __('Create Snapshot', 'wp-reset') . '</a>';
1367 + }
1368 + if ($params['collapse_button']) {
1369 + $out .= '<a class="toggle-card tooltip" href="#" title="' . __('Collapse / expand box', 'wp-reset') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a>';
1370 + }
1371 + $out .= '</div></h4>';
936 1372
937 - echo '<b>' . __('What happens when I click the Reset button?', 'wp-reset') . '</b>';
938 - echo '<ul class="plain-list">';
939 - echo '<li>' . __('you will have to confirm the action one more time because there is NO UNDO', 'wp-reset') . '</li>';
940 - echo '<li>' . __('everything will be reset; see bullets above for details', 'wp-reset') . '</li>';
941 - echo '<li>' . __('site title, WordPress address, site address, site language, search engine visibility and current user will be restored', 'wp-reset') . '</li>';
942 - echo '<li>' . __('you will be logged out, automatically logged in and taken to the admin dashboard', 'wp-reset') . '</li>';
943 - echo '<li>' . __('WP Reset plugin will be reactivated if that option is chosen in the <a href="#card-post-reset">post-reset options</a>', 'wp-reset') . '</li>';
944 - echo '</ul>';
1373 + return $out;
1374 + } // get_card_header
945 1375
946 - echo '<b>' . __('WP-CLI Support', 'wp-reset') . '</b>';
947 - echo '<p>' . 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'), '<code>wp help reset</code>');
948 - 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'), '<code>--yes</code>') . '</p>';
949 - echo '</div>';
950 1376
951 - $theme = wp_get_theme();
1377 + /**
1378 + * Generate tool icons and description detailing what it modifies
1379 + *
1380 + * @param bool $modify_files Does the tool modify files?
1381 + * @param bool $modify_db Does the tool modify the database?
1382 + * @param bool $plural Is there more than one tool in the set?
1383 + *
1384 + * @return string
1385 + */
1386 + function get_tool_icons($modify_files = false, $modify_db = false, $plural = false)
1387 + {
1388 + $out = '';
1389 + $modify_files = (bool) $modify_files;
1390 + $modify_db = (bool) $modify_db;
1391 + $plural = (bool) $plural;
952 1392
953 - echo '<div class="card" id="card-post-reset">';
954 - echo '<a class="toggle-card" href="#" title="' . __('Collapse / expand box', 'wp-reset') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a>';
955 - echo '<h2>' . __('Post-reset actions', 'wp-reset') . '</h2>';
956 - echo '<p><label for="reactivate-theme"><input name="wpr-post-reset[reactivate_theme]" type="checkbox" id="reactivate-theme" value="1"> ' . __('Reactivate current theme', 'wp-reset') . ' - ' . $theme->get('Name') . '</label></p>';
957 - echo '<p><label for="reactivate-wpreset"><input name="wpr-post-reset[reactivate_wpreset]" type="checkbox" id="reactivate-wpreset" value="1" checked> ' . __('Reactivate WP Reset plugin', 'wp-reset') . '</label></p>';
958 - echo '<p><label for="reactivate-plugins"><input name="wpr-post-reset[reactivate_plugins]" type="checkbox" id="reactivate-plugins" value="1"> ' . __('Reactivate all currently active plugins', 'wp-reset') . '</label></p>';
959 - echo '</div>';
1393 + $out .= '<p class="tool-icons">';
1394 + $out .= '<i class="icon-doc-text-inv' . ($modify_files ? ' red' : '') . '"></i> ';
1395 + $out .= '<i class="icon-database' . ($modify_db ? ' red' : '') . '"></i> ';
960 1396
961 - echo '<div class="card">';
962 - echo '<h2>' . __('Reset', 'wp-reset') . '</h2>';
963 - echo '<p>' . __('Type <b>reset</b> in the confirmation field to confirm the reset and then click the "Reset WordPress" button. <b>There is NO UNDO. No backups are made by WP Reset.</b>', 'wp-reset') . '</p>';
1397 + if ($plural) {
1398 + if ($modify_files && $modify_db) {
1399 + $out .= 'these tools <b>modify files &amp; the database</b>';
1400 + } elseif (!$modify_files && $modify_db) {
1401 + $out .= 'these tools <b>modify the database</b> but they don\'t modify any files</b>';
1402 + } elseif ($modify_files && !$modify_db) {
1403 + $out .= 'these tools <b>modify files</b> but they don\'t modify the database</b>';
1404 + }
1405 + } else {
1406 + if ($modify_files && $modify_db) {
1407 + $out .= 'this tool <b>modifies files &amp; the database</b>';
1408 + } elseif (!$modify_files && $modify_db) {
1409 + $out .= 'this tool <b>modifies the database</b> but it doesn\'t modify any files</b>';
1410 + } elseif ($modify_files && !$modify_db) {
1411 + $out .= 'this tool <b>modifies files</b> but it doesn\'t modify the database</b>';
1412 + } else {
1413 + $out .= 'this tool doesn\'t modify files or the database';
1414 + }
1415 + }
1416 + $out .= '</p>';
964 1417
965 - wp_nonce_field('wp-reset');
966 - echo '<p><input id="wp_reset_confirm" type="text" name="wp_reset_confirm" placeholder="' . esc_attr__('Type in "reset"', 'wp-reset'). '" value="" autocomplete="off"> &nbsp;';
967 - echo '<input id="wp_reset_submit" type="button" class="button-primary" value="' . __('Reset WordPress', 'wp-reset') . '"></p>';
968 - echo '</div>';
969 - } // tab_reset
1418 + return $out;
1419 + } // get_tool_icons
970 1420
971 1421
972 - /**
973 - * Echoes content for tools tab
974 - *
975 - * @return null
976 - */
977 - private function tab_tools() {
978 - global $wpdb;
1422 + /**
1423 + * Outputs complete plugin's admin page
1424 + *
1425 + * @return null
1426 + */
1427 + function plugin_page()
1428 + {
1429 + // double check for admin privileges
1430 + if (!current_user_can('manage_options')) {
1431 + wp_die(esc_html__('Sorry, you are not allowed to access this page.', 'wp-reset'));
1432 + }
979 1433
980 - echo '<div class="card">';
981 - echo '<h2>' . __('Delete Transients', 'wp-reset') . '</h2>';
982 - echo '<p>' . __('All transient related database entries will be deleted. Including expired and non-expired transients, and orphaned transient timeout entries.<br><b>There is NO UNDO. WP Reset does not make any backups.</b>', 'wp-reset') . '</p>';
983 - echo '<p><a data-btn-confirm="Delete all transients" data-text-wait="Deleting transients. Please wait." data-text-confirm="All database entries related to transients will be deleted. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n transient database entries have been deleted." class="button button-delete" href="#" id="delete-transients">Delete all transients</a></p>';
984 - echo '</div>';
1434 + echo '<div class="wrap">';
1435 + echo '<form id="wp_reset_form" action="' . esc_url(admin_url('tools.php?page=wp-reset')) . '" method="post" autocomplete="off">';
985 1436
986 - $upload_dir = wp_upload_dir(date('Y/m'), true);
987 - $upload_dir['basedir'] = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $upload_dir['basedir']);
1437 + echo '<header>';
1438 + echo '<div class="wpr-container">';
1439 + echo '<img id="logo-icon" src="' . esc_url($this->plugin_url) . 'img/wp-reset-logo.png" title="' . esc_html__('WP Reset', 'wp-reset') . '" alt="' . esc_html__('WP Reset', 'wp-reset') . '">';
1440 + echo '</div>';
1441 + echo '</header>';
988 1442
989 - echo '<div class="card">';
990 - echo '<h2>' . __('Clean Uploads Folder', 'wp-reset') . '</h2>';
991 - echo '<p>' . __('All files in <code>' . $upload_dir['basedir'] . '</code> folder will be deleted. Including folders and subfolders, and files in subfolders. Files associated with <a href="' . admin_url('upload.php') . '">media</a> entries will be deleted too.<br><b>There is NO UNDO. WP Reset does not make any backups.</b>', 'wp-reset') . '</p>';
992 - if (false != $upload_dir['error']) {
993 - echo '<p><span style="color:#dd3036;"><b>Tool is not available.</b></span> Folder is not writeable by WordPress. Please check file and folder access rights.</p>';
994 - } else {
995 - echo '<p><a data-btn-confirm="Delete everything in uploads folder" data-text-wait="Deleting uploads. Please wait." data-text-confirm="All files and folders in uploads will be deleted. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n files &amp; folders have been deleted." class="button button-delete" href="#" id="delete-uploads">Delete all files &amp; folders in uploads folder</a></p>';
996 - }
997 - echo '</div>';
1443 + echo '<div id="loading-tabs"><img class="rotating" src="' . esc_url($this->plugin_url) . 'img/wp-reset-icon.png' . '" alt="Loading. Please wait." title="Loading. Please wait."></div>';
998 1444
999 - $theme = wp_get_theme();
1445 + echo '<div id="wp-reset-tabs" class="ui-tabs" style="display: none;">';
1000 1446
1001 - echo '<div class="card">';
1002 - echo '<h2>' . __('Delete Themes', 'wp-reset') . '</h2>';
1003 - echo '<p>' . __('All themes will be deleted. Including the currently active theme - ' . $theme->get('Name') . '.<br><b>There is NO UNDO. WP Reset does not make any backups.</b>', 'wp-reset') . '</p>';
1004 - echo '<p><a data-btn-confirm="Delete all themes" data-text-wait="Deleting all themes. Please wait." data-text-confirm="All themes will be deleted. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n themes have been deleted." class="button button-delete" href="#" id="delete-themes">Delete all themes</a></p>';
1005 - echo '</div>';
1447 + echo '<nav>';
1448 + echo '<div class="wpr-container">';
1449 + echo '<ul class="wpr-main-tab">';
1450 + echo '<li><a href="#tab-reset">' . esc_html(__('Reset', 'wp-reset')) . '</a></li>';
1451 + echo '<li><a href="#tab-tools">' . esc_html(__('Tools', 'wp-reset')) . '</a></li>';
1452 + echo '<li><a href="#tab-snapshots">' . esc_html(__('Snapshots', 'wp-reset')) . '</a></li>';
1453 + echo '<li><a href="#tab-collections">' . esc_html(__('Collections', 'wp-reset')) . '</a></li>';
1454 + echo '<li><a href="#tab-support">' . esc_html(__('Support', 'wp-reset')) . '</a></li>';
1455 + echo '<li class="tab-pro"><a href="#tab-pro">' . esc_html(__('PRO', 'wp-reset')) . '</a></li>';
1456 + echo '</ul>';
1457 + echo '</div>'; // container
1458 + echo '</nav>';
1006 1459
1007 - echo '<div class="card">';
1008 - echo '<h2>' . __('Delete Plugins', 'wp-reset') . '</h2>';
1009 - echo '<p>' . __('All plugins will be deleted except for WP Reset which will remain active.<br><b>There is NO UNDO. WP Reset does not make any backups.</b>', 'wp-reset') . '</p>';
1010 - echo '<p><a data-btn-confirm="Delete plugins" data-text-wait="Deleting plugins. Please wait." data-text-confirm="All plugins except WP Reset will be deleted. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n plugins have been deleted." class="button button-delete" href="#" id="delete-plugins">Delete plugins</a></p>';
1011 - echo '</div>';
1460 + echo '<div id="wpr-notifications">';
1461 + echo '<div class="wpr-container">';
1462 + $this->custom_notifications();
1463 + echo '</div>';
1464 + echo '</div>'; // wpr-notifications
1012 1465
1013 - $custom_tables = $this->get_custom_tables();
1466 + // tabs
1467 + echo '<div class="wpr-container">';
1468 + echo '<div id="wpr-content">';
1014 1469
1015 - echo '<div class="card">';
1016 - echo '<h2>' . __('Empty or Delete Custom Tables', 'wp-reset') . '</h2>';
1017 - echo '<p>' . __('This action affects only custom tables with <code>' . $wpdb->prefix . '</code> prefix. Core WP tables and other tables in the database that do not have that prefix will not be deleted/emptied. Deleting (dropping) tables completely removes them from the database. Emptying (truncating) removes all content from them, but keeps the structure intact.<br><b>There is NO UNDO. WP Reset does not make any backups.</b></p>', 'wp-reset');
1018 - if ($custom_tables) {
1019 - echo '<p>' . __('The following ' . sizeof($custom_tables) . ' custom tables are affected by this tool: ');
1020 - foreach ($custom_tables as $tbl) {
1021 - echo '<code>' . $tbl['name'] . '</code>';
1022 - if (next($custom_tables)) {
1023 - echo ', ';
1470 + echo '<div style="display: none;" id="tab-reset">';
1471 + $this->tab_reset();
1472 + echo '</div>';
1473 +
1474 + echo '<div style="display: none;" id="tab-tools">';
1475 + $this->tab_tools();
1476 + echo '</div>';
1477 +
1478 + echo '<div style="display: none;" id="tab-snapshots">';
1479 + $this->tab_snapshots();
1480 + echo '</div>';
1481 +
1482 + echo '<div style="display: none;" id="tab-collections">';
1483 + $this->tab_collections();
1484 + echo '</div>';
1485 +
1486 + echo '<div style="display: none;" id="tab-support">';
1487 + $this->tab_support();
1488 + echo '</div>';
1489 +
1490 + echo '<div style="display: none;" id="tab-pro">';
1491 + $this->tab_pro();
1492 + echo '</div>';
1493 +
1494 + echo '</div>'; // content
1495 + echo '</div>'; // container
1496 + echo '</div>'; // wp-reset-tabs
1497 +
1498 + echo '</form>';
1499 +
1500 + echo '<div id="wpr-sidebar-ads">';
1501 + echo '<div id="wpr-ad">';
1502 + echo '<h3 class="textcenter"><b>Save time &amp; money with WP Reset PRO! First WP dev tool for non-devs.</b></h3>';
1503 + echo '<p class="textcenter"><a href="#" data-feature="sidebar-logo" class="button-pro-feature textcenter"><img style="max-width: 90%;" src="' . esc_url($this->plugin_url) . '/img/wp-reset-logo.png" alt="WP Reset PRO" title="WP Reset PRO"></a></p>';
1504 + echo '<ul class="plain-list">
1505 + <li>25+ Reset Tools</li>
1506 + <li>Plugins &amp; Themes Collections</li>
1507 + <li>Automatic Snapshots</li>
1508 + <li>WP Reset Cloud, Dropbox &amp; Google Drive support</li>
1509 + <li>Emergency Recovery Script</li>
1510 + <li>White-label Mode + Complete Plugin Rebranding</li>
1511 + <li>Licenses &amp; Sites Manager (remote SaaS dashboard)</li>
1512 + <li>Friendly email support from plugin developers</li>
1513 + </ul>';
1514 + echo '<p class="textcenter"><a href="#" data-feature="sidebar-button" class="button-pro-feature button button-primary">Get PRO now</a></p>';
1515 + echo '</div>';
1516 +
1517 + if (!defined('WPFSSL_OPTIONS_KEY')) {
1518 + echo '<div id="wpfssl-ad">';
1519 + echo '<h3 class="textcenter"><b>Problems with SSL certificate?<br>Moving a site from HTTP to HTTPS?<br>Mixed content giving you troubles?<br><br><u>Fix all SSL problems with one plugin!</u></b></h3>';
1520 + echo '<p class="textcenter"><a href="#" class="textcenter install-wpfssl"><img style="max-width: 90%;" src="' . esc_url($this->plugin_url) . '/img/wp-force-ssl-logo.png" alt="WP Force SSL" title="WP Force SSL"></a></p>';
1521 + echo '<p class="textcenter"><br><a href="#" class="install-wpfssl button button-primary">Install &amp; activate the free WP Force SSL plugin</a></p><p><a href="https://wordpress.org/plugins/wp-force-ssl/" target="_blank">WP Force SSL</a> is a free WP plugin maintained by the same team as this Maintenance plugin. It has <b>+150,000 users, 5-star rating</b>, and is hosted on the official WP repository.</p>';
1522 + echo '</div>';
1024 1523 }
1025 - } // foreach
1026 - echo '.</p>';
1027 - $custom_tables_btns = '';
1028 - } else {
1029 - echo '<p>' . __('There are no custom tables. There\'s nothing for this tool to empty or delete.', 'wp-reset') . '</p>';
1030 - $custom_tables_btns = ' disabled';
1031 - }
1032 - echo '<p><a data-btn-confirm="Empty custom tables" data-text-wait="Emptying custom tables. Please wait." data-text-confirm="All custom tables with prefix <code>' . $wpdb->prefix . '</code> will be emptied. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n custom tables have been emptied." class="button button-delete' . $custom_tables_btns . '" href="#" id="truncate-custom-tables">Empty (truncate) custom tables</a>&nbsp; &nbsp;';
1033 - echo '<a data-btn-confirm="Delete custom tables" data-text-wait="Deleting custom tables. Please wait." data-text-confirm="All custom tables with prefix <code>' . $wpdb->prefix . '</code> will be deleted. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n custom tables have been deleted." class="button button-delete' . $custom_tables_btns . '" href="#" id="drop-custom-tables">Delete (drop) custom tables</a></p>';
1524 + echo '</div>';
1034 1525
1035 - echo '</div>';
1036 - } // tab_tools
1526 + echo '</div>'; // wrap
1527 + } // plugin_page
1037 1528
1038 1529
1039 - /**
1040 - * Echoes content for support tab
1041 - *
1042 - * @return null
1043 - */
1044 - private function tab_support() {
1045 - echo '<div class="card">';
1046 - echo '<h2>' . __('Public support forum', 'wp-reset') . '</h2>';
1047 - echo '<p>' . __('We are very active on the <a href="https://wordpress.org/support/plugin/wp-reset" target="_blank">official WP Reset support forum</a>. 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') . '</p>';
1048 - echo '</div>';
1530 + /**
1531 + * Echoes all custom plugin notitications
1532 + *
1533 + * @return null
1534 + */
1535 + private function custom_notifications()
1536 + {
1537 + $notice_shown = false;
1538 + $meta = $this->get_meta();
1539 + $snapshots = $this->get_snapshots();
1049 1540
1050 - echo '<div class="card">';
1051 - echo '<h2>' . __('Private contact', 'wp-reset') . '</h2>';
1052 - echo '<p>' . __('If there\'s a need to contact us privately send emails to <a href="mailto:wpreset@webfactoryltd.com">wpreset@webfactoryltd.com</a>. 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') . '</p>';
1053 - echo '</div>';
1541 + // update to PRO after activating the license
1542 + if ($this->license->is_active()) {
1543 + echo '<div class="card notice-wrapper notice-info">';
1544 + echo '<h2>' . esc_html(__('Thank you for purchasing WP Reset PRO!', 'wp-reset')) . '</h2>';
1545 + echo '<p>Your license has been verified &amp; activated.</b><br>To start using the PRO version, please follow these steps:';
1546 + echo '<ol>';
1547 + echo '<li><a href="https://dashboard.wpreset.com/pro-download/" target="_blank">Download</a> the latest version of the PRO plugin.</li>';
1548 + echo '<li>Go to <a href="' . esc_url(admin_url('plugin-install.php')) . '">Plugins - Add New - Upload Plugin</a> and upload the ZIP you just downloaded.</li>';
1549 + echo '<li>If asked to replace (overwrite) the free version - confirm it.</li>';
1550 + echo '<li>Activate the plugin.</li>';
1551 + echo '<li>That\'s it, no more steps.</li>';
1552 + echo '</ol>';
1553 + echo '</div>';
1554 + $notice_shown = true;
1555 + }
1054 1556
1055 - echo '<div class="card">';
1056 - echo '<h2>' . __('Care to help out?', 'wp-reset') . '</h2>';
1057 - echo '<p>' . __('No need for donations or anything like that :) If you can give us a <a href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post" target="_blank">five star rating</a> you\'ll help out more than you can imagine. Thank you!', 'wp-reset') . '</p>';
1058 - echo '</div>';
1059 - } // tab_support
1557 + // warn that WPR is not WPMU compatible
1558 + if (false === $notice_shown && is_multisite()) {
1559 + echo '<div class="card notice-wrapper notice-error">';
1560 + echo '<h2>' . esc_html__('WP Reset is not compatible with multisite!', 'wp-reset') . '</h2>';
1561 + WP_Reset_Utility::wp_kses_wf('<p>' . __('Please be careful when using WP Reset with multisite enabled. It\'s not recommended to reset the main site. Sub-sites should be OK. We\'re working on making it fully compatible with WP-MU. <b>Till then please be careful.</b> Thank you for understanding.', 'wp-reset') . '</p>');
1562 + echo '</div>';
1563 + $notice_shown = true;
1564 + }
1060 1565
1566 + // ask for review
1567 + if ((!empty($meta['reset_count']) || !empty($snapshots) || current_time('timestamp', true) - $meta['first_install'] > DAY_IN_SECONDS)
1568 + && false === $notice_shown
1569 + && false == $this->get_dismissed_notices('rate')
1570 + ) {
1571 + echo '<div class="card notice-wrapper notice-info">';
1572 + echo '<h2>' . esc_html__('Please help us spread the word &amp; keep the plugin up-to-date', 'wp-reset') . '</h2>';
1573 + WP_Reset_Utility::wp_kses_wf('<p>' . __('If you use &amp; enjoy WP Reset, <b>please rate it on WordPress.org</b>. It only takes a second and helps us keep the plugin maintained. Thank you!', 'wp-reset') . '</p>');
1574 + echo '<p><a class="button-primary button" title="' . esc_html__('Rate WP Reset', 'wp-reset') . '" target="_blank" href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post">' . esc_html__('Rate the plugin ★★★★★', 'wp-reset') . '</a> <a href="#" class="wpr-dismiss-notice dismiss-notice-rate" data-notice="rate">' . esc_html__('I\'ve already rated it', 'wp-reset') . '</a></p>';
1575 + echo '</div>';
1576 + $notice_shown = true;
1577 + }
1578 + } // custom_notifications
1061 1579
1062 - /**
1063 - * Echoes content for snapshots tab
1064 - *
1065 - * @return null
1066 - */
1067 - private function tab_snapshots() {
1068 - global $wpdb;
1069 - $tbl_core = $tbl_custom = $tbl_size = $tbl_rows = 0;
1070 1580
1071 - echo '<div class="card" id="card-snapshots">';
1072 - echo '<a class="toggle-card" href="#" title="' . __('Collapse / expand box', 'wp-reset') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a>';
1073 - echo '<h2>' . __('Database Snapshots', 'wp-reset') . '</h2>';
1074 - echo '<p>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.<br>
1075 - 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 <a href="https://wordpress.org/plugins/updraftplus/" target="_blank">UpdraftPlus</a>. 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.<br>Restoring a snapshot does not affect other snapshots, or WP Reset settings.</p>';
1076 - echo '<p>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 <a href="https://twitter.com/WebFactoryLtd" target="_blank">@webfactoryltd</a> or <a href="mailto:wpreset@webfactoryltd.com?subject=WPR%20DB%20Snapshots%20Feedback">email us</a>. Thank you!</p>';
1581 + /**
1582 + * Echoes content for reset tab
1583 + *
1584 + * @return null
1585 + */
1586 + private function tab_reset()
1587 + {
1588 + global $current_user, $wpdb;
1077 1589
1078 - $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1079 - if (is_array($table_status)) {
1080 - foreach ($table_status as $index => $table) {
1081 - if (0 !== stripos($table->Name, $wpdb->prefix)) {
1082 - continue;
1590 + echo '<div class="card">';
1591 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Please read carefully before proceeding', 'wp-reset'), 'reset-description', array('collapse_button' => true)));
1592 + echo '<div class="card-body">';
1593 + echo '<p>The following table details what data will be deleted (reset or destroyed) when a selected reset tool is run. Please read it! ';
1594 + echo 'If something is not clear <a href="#" class="change-tab" data-tab="4">contact support</a> before running any tools. It\'s better to ask than to be sorry!';
1595 + echo '</p>';
1596 + echo '<p><i class="dashicons dashicons-trash red tooltip" title="Tool WILL delete, reset or destroy the noted data" style="vertical-align: bottom;"></i> - tool WILL delete, reset or destroy the noted data<br>';
1597 + echo '<i class="dashicons dashicons-yes tooltip" title="Tool will NOT touch the noted data in any way" style="vertical-align: bottom;"></i> - tool will NOT touch the noted data in any way</p>';
1598 +
1599 + echo '<table id="reset-details" class="">';
1600 + echo '<tr>';
1601 + echo '<th>&nbsp;</th>';
1602 + echo '<th>Options Reset<br><a data-feature="tool-options-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1603 + echo '<th nowrap>Site Reset</th>';
1604 + echo '<th>Nuclear Reset<br><a data-feature="tool-nuclear-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1605 + echo '</tr>';
1606 +
1607 + $rows = array();
1608 + $rows['Posts, pages & custom post types'] = array(0, 1, 1);
1609 + $rows['Comments'] = array(0, 1, 1);
1610 + $rows['Media'] = array(0, 1, 1);
1611 + $rows['Media files'] = array(0, 0, 1);
1612 + $rows['Users'] = array(0, 1, 1);
1613 + $rows['User roles'] = array(1, 1, 1);
1614 + $rows['Current user - ' . $current_user->user_login] = array(0, 0, 0);
1615 + $rows['Widgets'] = array(1, 1, 1);
1616 + $rows['Transients'] = array(1, 1, 1);
1617 + $rows['Settings &amp; options (from WP, plugins & themes)'] = array(1, 1, 1);
1618 + $rows['Site title, WP address, site address,' . PHP_EOL . 'search engine visibility, timezone'] = array(0, 0, 0);
1619 + $rows['Site language'] = array(0, 0, 1);
1620 + $rows['Data in all default WP tables'] = array(0, 1, 1);
1621 + $rows['Custom database tables with prefix ' . $wpdb->prefix] = array(0, 1, 1);
1622 + $rows['Other database tables'] = array(0, 0, 0);
1623 + $rows['Plugin files'] = array(0, 0, 1);
1624 + $rows['MU plugin files'] = array(0, 0, 1);
1625 + $rows['Drop-in files'] = array(0, 0, 1);
1626 + $rows['Theme files'] = array(0, 0, 1);
1627 + $rows['All files in uploads'] = array(0, 0, 1);
1628 + $rows['Custom folders in wp-content'] = array(0, 0, 1);
1629 +
1630 + foreach ($rows as $tool => $opt) {
1631 + echo '<tr>';
1632 + echo '<td>';
1633 + WP_Reset_Utility::wp_kses_wf(nl2br(esc_html($tool)));
1634 + echo '</td>';
1635 + if (empty($opt[0])) {
1636 + echo '<td><i class="dashicons dashicons-yes tooltip" title="Data will NOT be deleted, reset or modified"></i></td>';
1637 + } else {
1638 + echo '<td><i class="dashicons dashicons-trash red tooltip" title="Data WILL BE deleted, reset or modified"></i></td>';
1639 + }
1640 + if (empty($opt[1])) {
1641 + echo '<td><i class="dashicons dashicons-yes tooltip" title="Data will NOT be deleted, reset or modified"></i></td>';
1642 + } else {
1643 + echo '<td><i class="dashicons dashicons-trash red tooltip" title="Data WILL BE deleted, reset or modified"></i></td>';
1644 + }
1645 + if (empty($opt[2])) {
1646 + echo '<td><i class="dashicons dashicons-yes tooltip" title="Data will NOT be deleted, reset or modified"></i></td>';
1647 + } else {
1648 + echo '<td><i class="dashicons dashicons-trash red tooltip" title="Data WILL BE deleted, reset or modified"></i></td>';
1649 + }
1650 + echo '</tr>';
1651 + } // foreach $rows
1652 + echo '<tfoot>';
1653 + echo '<tr>';
1654 + echo '<th>&nbsp;</th>';
1655 + echo '<th>Options Reset<br><a data-feature="tool-options-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1656 + echo '<th nowrap>Site Reset</th>';
1657 + echo '<th>Nuclear Reset<br><a data-feature="tool-nuclear-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1658 + echo '</tr>';
1659 + echo '</tfoot>';
1660 + echo '</table>';
1661 +
1662 + echo '<p><b>' . esc_html__('What happens when I run any Reset tool?', 'wp-reset') . '</b></p>';
1663 + echo '<ul class="plain-list">';
1664 + WP_Reset_Utility::wp_kses_wf('<li>' . __('remember, always <b>make a backup first</b> or use <a href="#" class="change-tab" data-tab="2">snapshots</a>', 'wp-reset') . '</li>');
1665 + echo '<li>' . esc_html__('you will have to confirm the action one more time', 'wp-reset') . '</li>';
1666 + echo '<li>' . esc_html__('see the table above to find out what exactly will be reset or deleted', 'wp-reset') . '</li>';
1667 + echo '<li>' . esc_html__('site title, WordPress URL, site URL, site language, search engine visibility and current user will always be restored', 'wp-reset') . '</li>';
1668 + echo '<li>' . esc_html__('you will be logged out, automatically logged back in and taken to the admin dashboard', 'wp-reset') . '</li>';
1669 + echo '<li>' . esc_html__('WP Reset plugin will be reactivated if that option is chosen', 'wp-reset') . '</li>';
1670 + echo '</ul>';
1671 +
1672 + echo '<p><b>' . esc_html__('WP-CLI Support', 'wp-reset') . '</b><br>';
1673 + /* translators: %s is replaced with the literal html "<code>wp help reset</code>" */
1674 + echo '' . sprintf(esc_html__('All tools 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'), '<code>wp help reset</code>');
1675 + /* translators: %s is replaced with the literal html "<code>--yes</code>" */
1676 + echo sprintf(esc_html__('All actions have to be confirmed. If you want to skip confirmation use the standard %s option. Please be careful and backup first.', 'wp-reset'), '<code>--yes</code>') . '</p>';
1677 +
1678 + echo '</div></div>'; // card description
1679 +
1680 + $theme = wp_get_theme();
1681 + $theme_name = $theme->get('Name');
1682 + if (empty($theme_name)) {
1683 + $theme_name = '<i>no active theme</i>';
1083 1684 }
1084 - if (empty($table->Engine)) {
1085 - continue;
1685 + $active_plugins = get_option('active_plugins');
1686 +
1687 + // options reset
1688 + echo '<div class="card">';
1689 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Options Reset', 'wp-reset'), 'tool-options-reset', array('collapse_button' => true, 'pro' => true)));
1690 + echo '<div class="card-body">';
1691 + echo '<p>Options table will be reset to default values meaning all WP core settings, widgets, theme settings and customizations, and plugin settings will be gone. Other content and files will not be touched including posts, pages, custom post types, comments and other data stored in separate tables. Site URL and name will be kept as well. Please see the <a href="#reset-details" class="scrollto">table above</a> for details.</p>';
1692 +
1693 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1694 +
1695 + echo '<p><br><label for="reset-options-reactivate-theme"><input type="checkbox" id="reset-options-reactivate-theme" value="1"> ' . esc_html__('Reactivate current theme', 'wp-reset') . ' - ' . esc_html($theme_name) . '</label></p>';
1696 + echo '<p><label for="reset-options-reactivate-plugins"><input type="checkbox" id="reset-options-reactivate-plugins" value="1"> Reactivate ' . esc_attr(sizeof($active_plugins)) . ' currently active plugin' . (sizeof($active_plugins) != 1 ? 's' : '') . ' (WP Reset will reactivate by default)</label></p>';
1697 +
1698 + echo '<p class="mb0"><a class="button button-delete button-pro-feature" href="#">Reset all options - <span data-feature="tool-options-reset" class="pro-feature"><span class="pro">PRO</span> tool</span></a></p>';
1699 + echo '</div>';
1700 + echo '</div>'; // options reset
1701 +
1702 + echo '<div class="card">';
1703 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Site Reset', 'wp-reset'), 'tool-site-reset', array('collapse_button' => true)));
1704 + echo '<div class="card-body">';
1705 + echo '<p><label for="reactivate-theme"><input name="wpr-post-reset[reactivate_theme]" type="checkbox" id="reactivate-theme" value="1"> ' . esc_html__('Reactivate current theme', 'wp-reset') . ' - ' . esc_html($theme->get('Name')) . '</label></p>';
1706 + echo '<p><label for="reactivate-wpreset"><input name="wpr-post-reset[reactivate_wpreset]" type="checkbox" id="reactivate-wpreset" value="1" checked> ' . esc_html__('Reactivate WP Reset plugin', 'wp-reset') . '</label></p>';
1707 +
1708 + echo '<p><label for="reactivate-plugins"><input name="wpr-post-reset[reactivate_plugins]" type="checkbox" id="reactivate-plugins" value="1"> ' . esc_html__('Reactivate all currently active plugins', 'wp-reset') . '</label></p>';
1709 + WP_Reset_Utility::wp_kses_wf('<p>' . __('Type <b>reset</b> in the confirmation field to confirm the reset and then click the "Reset WordPress" button.<br>Always <a href="#" class="create-new-snapshot" data-description="Before resetting the site">create a snapshot</a> before resetting if you want to be able to undo.', 'wp-reset') . '</p>');
1710 +
1711 + wp_nonce_field('wp-reset');
1712 + /* translators: %s is replaced with "reset"*/
1713 + echo '<p class="mb0"><input id="wp_reset_confirm" type="text" name="wp_reset_confirm" placeholder="' . esc_attr(sprintf(__('Type in: %s', 'wp-reset'), '"reset"')) . '" value="" autocomplete="off"> &nbsp;';
1714 + echo '<a id="wp_reset_submit" class="button button-delete">' . esc_html__('Reset Site', 'wp-reset') . '</a>';
1715 + WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('reset-wordpress', 'Before resetting the site'));
1716 + echo '</p>';
1717 + echo '</div>';
1718 + echo '</div>'; // card reset
1719 +
1720 + // nuclear reset
1721 + echo '<div class="card">';
1722 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Nuclear Site Reset', 'wp-reset'), 'tool-nuclear-reset', array('collapse_button' => true, 'pro' => true)));
1723 + echo '<div class="card-body">';
1724 + echo '<p>All data will be deleted or reset (see the <a href="#reset-details" class="scrollto">explanation table</a> for details). All data stored in the database including custom tables with <code>' . esc_html($wpdb->prefix) . '</code> prefix, as well as all files in wp-content, themes and plugins folders. The only thing restored after reset will be your user account so you can log in again, and the basic WP settings like site URL. Please see the <a href="#reset-details" class="scrollto">table above</a> for details.</p>';
1725 +
1726 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1727 +
1728 + if (is_multisite()) {
1729 + echo '<p class="mb0 wpmu-error">This tool is <b>not compatible</b> with WP multisite (WPMU). Using it would delete files shared by multiple sites in the WP network.</p>';
1730 + } else {
1731 + echo '<p><br><label for="nuclear-reset-reactivate-wpreset"><input type="checkbox" id="nuclear-reset-reactivate-wpreset" value="1" checked> ' . esc_html__('Reactivate WP Reset plugin', 'wp-reset') . '</label></p>';
1732 +
1733 + WP_Reset_Utility::wp_kses_wf('<p>' . __('Type <b>reset</b> in the confirmation field to confirm the reset and then click the "Reset WordPress &amp; Delete All Custom Files &amp; Data" button. <b>There is NO UNDO.', 'wp-reset') . '</b></p>');
1734 +
1735 + echo '<p class="mb0"><input id="nuclear_reset_confirm" type="text" placeholder="' . esc_html__('Type in "reset"', 'wp-reset') . '" value="" autocomplete="off"> &nbsp;';
1736 + echo '<a class="button button-delete button-pro-feature" href="#">' . esc_html__('Reset WordPress &amp; Delete All Custom Files &amp; Data', 'wp-reset') . ' - <span data-feature="tool-nuclear-reset" class="pro-feature"><span class="pro">PRO</span> tool</span></a></p>';
1086 1737 }
1738 + echo '</div>';
1739 + echo '</div>'; // nuclear reset
1740 + } // tab_reset
1087 1741
1088 - $tbl_rows += $table->Rows;
1089 - $tbl_size += $table->Data_length + $table->Index_length;
1090 - if (in_array($table->Name, $this->core_tables)) {
1091 - $tbl_core++;
1742 +
1743 + /**
1744 + * Echoes content for tools tab
1745 + *
1746 + * @return null
1747 + */
1748 + private function tab_tools()
1749 + {
1750 + global $wpdb, $wp_version, $wp_filesystem;
1751 + $this->wp_init_filesystem();
1752 +
1753 + $tools = array(
1754 + 'tool-reset-theme-options' => 'Reset Theme Options',
1755 + '_tool-reset-user-roles' => 'Reset User Roles',
1756 + 'tool-delete-transients' => 'Delete Transients',
1757 + 'tool-purge-cache' => 'Purge Cache',
1758 + 'tool-delete-local-data' => 'Delete Local Data',
1759 + '_tool-delete-content' => 'Delete Content',
1760 + '_tool-delete-widgets' => 'Delete Widgets',
1761 + 'tool-delete-themes' => 'Delete Themes',
1762 + 'tool-delete-plugins' => 'Delete Plugins',
1763 + '_tool-delete-mu-plugins-dropins' => 'Delete MU Plugins & Drop-ins',
1764 + 'tool-delete-uploads' => 'Clean uploads Folder',
1765 + '_tool-delete-wp-content' => 'Clean wp-content Folder',
1766 + 'tool-empty-delete-custom-tables' => 'Empty or Delete Custom Tables',
1767 + '_tool-switch-wp-version' => 'Switch WP Version',
1768 + 'tool-delete-htaccess' => 'Delete .htaccess File'
1769 + );
1770 +
1771 + echo '<div class="card">';
1772 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Index of Tools', 'wp-reset'), 'iot', array('collapse_button' => true)));
1773 + echo '<div class="card-body">';
1774 + $i = 0;
1775 + $tools_nb = sizeof($tools);
1776 + foreach ($tools as $tool_id => $tool_name) {
1777 + if ($i == 0) {
1778 + echo '<div class="third">';
1779 + echo '<ul class="mb0 plain-list">';
1780 + }
1781 + if ($i == 5 || $i == 10) {
1782 + echo '</div>';
1783 + echo '<div class="third">';
1784 + echo '<ul class="mb0 plain-list">';
1785 + }
1786 +
1787 + if ($tool_id[0] == '_') {
1788 + $tool_id = ltrim($tool_id, '_');
1789 + echo '<li><a title="Jump to ' . esc_attr($tool_name) . ' tool" class="scrollto" href="#' . esc_attr($tool_id) . '">' . esc_html($tool_name) . '</a> <a class="pro-feature" href="#" data-feature="' . esc_attr($tool_id) . '"><span class="pro">PRO</span> tool</a></li>';
1790 + } else {
1791 + echo '<li><a title="Jump to ' . esc_attr($tool_name) . ' tool" class="scrollto" href="#' . esc_attr($tool_id) . '">' . esc_html($tool_name) . '</a></li>';
1792 + }
1793 +
1794 + if ($i == $tools_nb - 1) {
1795 + echo '</ul>';
1796 + echo '</div>'; // third
1797 + }
1798 + $i++;
1799 + } // foreach tools
1800 + echo '</div>';
1801 + echo '</div>';
1802 +
1803 + echo '<div class="card">';
1804 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Reset Theme Options', 'wp-reset'), 'tool-reset-theme-options', array('iot_button' => true, 'collapse_button' => true)));
1805 + echo '<div class="card-body">';
1806 + WP_Reset_Utility::wp_kses_wf('<p>' . __('All options (mods) for all themes will be reset; not just for the active theme. The tool works only for themes that use the <a href="https://codex.wordpress.org/Theme_Modification_API" target="_blank">WordPress theme modification API</a>. If options are saved in some other, custom way they won\'t be reset.<br> Always <a href="#" class="create-new-snapshot" data-description="Before resetting theme options">create a snapshot</a> before using this tool if you want to be able to undo its actions.', 'wp-reset') . '</p>');
1807 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1808 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to reset all theme options?" data-btn-confirm="Reset theme options" data-text-wait="Resetting theme options. Please wait." data-text-confirm="All options (mods) for all themes will be reset. Always ' . esc_attr('<a data-description="Before resetting theme options" href="#" class="create-new-snapshot">create a snapshot</a> if you want to be able to undo') . '." data-text-done="Options for %n themes have been reset." data-text-done-singular="Options for one theme have been reset." class="button button-delete" href="#" id="reset-theme-options">Reset theme options</a>';
1809 + WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('reset-theme-options', 'Before resetting theme options') . '</p>');
1810 + echo '</div>';
1811 + echo '</div>'; // reset theme options
1812 +
1813 + echo '<div class="card default-collapsed">';
1814 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Reset User Roles', 'wp-reset'), 'tool-reset-user-roles', array('collapse_button' => true, 'iot_button' => true, 'pro' => true)));
1815 + echo '<div class="card-body">';
1816 + echo '<p>Default user roles\' capatibilities will be reset to their default values. All custom roles will be deleted.<br>Users that had custom roles will not be assigned any default ones and might not be able to log in. Roles have to be (re)assigned to them manually.</p>';
1817 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1818 + echo '<p class="mb0"><a class="button button-delete button-pro-feature" href="#">Reset user roles - <span data-feature="tool-reset-user-roles" class="pro-feature"><span class="pro">PRO</span> tool</span></a>';
1819 + WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('reset-user-roles', 'Before resetting user roles') . '</p>');
1820 + echo '</div>';
1821 + echo '</div>'; // reset user roles
1822 +
1823 + echo '<div class="card">';
1824 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Transients', 'wp-reset'), 'tool-delete-transients', array('iot_button' => true, 'collapse_button' => true)));
1825 + echo '<div class="card-body">';
1826 + echo '<p>All transient related database entries will be deleted. Including expired and non-expired transients, and orphaned transient timeout entries.<br>Always <a href="#" data-description="Before deleting transients" class="create-new-snapshot">create a snapshot</a> before using this tool if you want to be able to undo its actions.</p>';
1827 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1828 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to delete all transients?" data-btn-confirm="Delete all transients" data-text-wait="Deleting transients. Please wait." data-text-confirm="All database entries related to transients will be deleted. Always ' . esc_attr('<a data-description="Before deleting transients" href="#" class="create-new-snapshot">create a snapshot</a> if you want to be able to undo') . '." data-text-done="%n transient database entries have been deleted." data-text-done-singular="One transient database entry has been deleted." class="button button-delete" href="#" id="delete-transients">Delete all transients</a>';
1829 + WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('delete-transients', 'Before deleting transients') . '</p>');
1830 + echo '</div>';
1831 + echo '</div>'; // delete transients
1832 +
1833 + echo '<div class="card">';
1834 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Purge Cache', 'wp-reset'), 'tool-purge-cache', array('collapse_button' => true, 'iot_button' => true)));
1835 + echo '<div class="card-body">';
1836 + echo '<p>All cache objects stored in both files and the database will be deleted. Along with WP object cache and transients, cache from the following plugins will be purged: W3 Total Cache, WP Cache, LiteSpeed Cache, Endurance Page Cache, SiteGround Optimizer, WP Fastest Cache and Swift Performance.</p>';
1837 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1838 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to purge all cache?" data-btn-confirm="Purge cache" data-text-wait="Purging cache. Please wait." data-text-confirm="All cache objects will be deleted. There is NO UNDO. WP Reset does not make any file backups." data-text-done="Cache has been purged." data-text-done-singular="Cache has been purged." class="button button-delete" href="#" id="purge-cache">Purge cache</a></p>';
1839 + echo '</div>';
1840 + echo '</div>'; // purge cache
1841 +
1842 + echo '<div class="card">';
1843 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Local Data', 'wp-reset'), 'tool-delete-local-data', array('collapse_button' => true, 'iot_button' => true)));
1844 + echo '<div class="card-body">';
1845 + echo '<p>All local storage and session storage data will be deleted. Cookies without a custom set path will be deleted as well. WP cookies are not touched, with Delete Local Data button.<br>Deleting all WordPress cookies (including authentication cookies) will delete all WP related cookies and user (you) will be logged out on the next page reload.
1846 + </p>';
1847 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, false));
1848 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to delete all local data?" data-btn-confirm="Delete local data" data-text-wait="Deleting local data. Please wait." data-text-confirm="All local data; cookies, local storage and local session will be deleted. There is NO UNDO. WP Reset does not make backups of local data." data-text-done="%n local data objects have been deleted." data-text-done-singular="One local data object has been deleted." class="button button-delete" href="#" id="delete-local-data">Delete local data</a><a data-confirm-title="Are you sure you want to delete all WP related cookies?" data-btn-confirm="Delete all WordPress cookies" data-text-wait="Deleting WP cookies. Please wait." data-text-confirm="All WP cookies including authentication ones will be deleted. You will have to log in again. There is NO UNDO. WP Reset does not make backups of cookies." data-text-done="All WP cookies have been deleted. Reload the page to login again." data-text-done-singular="All WP cookies have been deleted. Reload the page to login again." class="button button-delete" href="#" id="delete-wp-cookies">Delete all WordPress cookies</a></p>';
1849 + echo '</div>';
1850 + echo '</div>'; // delete local data
1851 +
1852 + echo '<div class="card default-collapsed">';
1853 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Content', 'wp-reset'), 'tool-delete-content', array('collapse_button' => true, 'iot_button' => true, 'pro' => true)));
1854 + echo '<div class="card-body">';
1855 + echo '<p>Besides content, all linked or child records (for selected content) will be deleted to prevent creating orphaned rows in the database. For instance, for posts that\'s posts, post meta, and comments related to posts. Delete process does not call any WP hooks such as <i>before_delete_post</i>. Choosing a post type or taxonomy does not delete that parent object it deletes the child objects. Parent objects are defined in code. If you want to remove them, remove their code definition. When media is deleted, files are left in the uploads folder. To delete files use the <a class="scrollto" href="#tool-delete-uploads">Clean uploads Folder</a> tool. Deleting users does not affect the current, logged in user account. All orphaned objects will be reassigned to him.</p>';
1856 +
1857 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1858 +
1859 + $post_types = get_post_types('', false, 'and');
1860 + $taxonomies = get_taxonomies('', false, 'and');
1861 +
1862 + echo '<p><select size="6" multiple id="delete-content-types">';
1863 + // phpcs:ignore db call warning as we are using uncommon queries
1864 + echo '<option value="_comments">Comments (' . ((int) $wpdb->get_var("SELECT COUNT(comment_id) FROM $wpdb->comments")) . ')</option>'; // phpcs:ignore
1865 + echo '<option value="_users">Users (' . ((int) $wpdb->get_var("SELECT COUNT(id) FROM $wpdb->users")) . ')</option>'; // phpcs:ignore
1866 + foreach ($post_types as $type) {
1867 + $count = wp_count_posts($type->name, 'readable');
1868 + $tmp = 0;
1869 + foreach ($count as $cnt) {
1870 + $tmp += (int) $cnt;
1871 + }
1872 + echo '<option value="' . esc_attr($type->name) . '">Post type - ' . esc_html($type->label . ' (' . $tmp) . ')</option>';
1873 + } // foreach post types
1874 + foreach ($taxonomies as $tax) {
1875 + echo '<option value="_tax_' . esc_attr($tax->name) . '">Taxonomy - ' . esc_html($tax->label . ' (' . wp_count_terms($tax->name)) . ')</option>';
1876 + } // foreach post types
1877 +
1878 + echo '</select><br>';
1879 + echo 'Select content object(s) you want to delete. Use ctrl + click to select multiple objects.</p>';
1880 +
1881 + echo '<p class="mb0"><a class="button button-delete button-pro-feature" href="#">Delete content - <span data-feature="tool-delete-content" class="pro-feature"><span class="pro">PRO</span> tool</span></a></p>';
1882 + echo '</div>';
1883 + echo '</div>'; // delete content
1884 +
1885 + echo '<div class="card default-collapsed">';
1886 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Widgets', 'wp-reset'), 'tool-delete-widgets', array('collapse_button' => true, 'iot_button' => true, 'pro' => true)));
1887 + echo '<div class="card-body">';
1888 + echo '<p>All widgets, orphaned, active and inactive ones, as well as widgets in active and inactive sidebars will be deleted including their settings. After deleting, WordPress will automatically recreate default, empty database entries related to widgets. So, no matter how many times users run the tool it will never return "no data deleted". That\'s expected and normal.</p>';
1889 +
1890 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1891 +
1892 + echo '<p class="mb0"><a class="button button-delete button-pro-feature" href="#">Delete widgets - <span data-feature="tool-delete-widgets" class="pro-feature"><span class="pro">PRO</span> tool</span></a></p>';
1893 + echo '</div>';
1894 + echo '</div>'; // delete widgets
1895 +
1896 + $theme = wp_get_theme();
1897 +
1898 + echo '<div class="card">';
1899 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Themes', 'wp-reset'), 'tool-delete-themes', array('iot_button' => true, 'collapse_button' => true)));
1900 + echo '<div class="card-body">';
1901 + /* translators: %s is replaced with the currently active theme name */
1902 + WP_Reset_Utility::wp_kses_wf('<p>' . sprintf(__('All themes will be deleted. Including the currently active theme - %s.<br><b>There is NO UNDO. WP Reset does not make any file backups.</b>', 'wp-reset'), esc_html($theme->get('Name'))) . '</p>');
1903 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1904 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to delete all themes?" data-btn-confirm="Delete all themes" data-text-wait="Deleting all themes. Please wait." data-text-confirm="All themes will be deleted. There is NO UNDO. WP Reset does not make any file backups." data-text-done="%n themes have been deleted." data-text-done-singular="One theme has been deleted." class="button button-delete" href="#" id="delete-themes">Delete all themes</a></p>';
1905 + echo '</div>';
1906 + echo '</div>'; // delete themes
1907 +
1908 + echo '<div class="card">';
1909 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Plugins', 'wp-reset'), 'tool-delete-plugins', array('iot_button' => true, 'collapse_button' => true)));
1910 + echo '<div class="card-body">';
1911 + WP_Reset_Utility::wp_kses_wf('<p>' . __('All plugins will be deleted except for WP Reset which will remain active.<br><b>There is NO UNDO. WP Reset does not make any file backups.</b>', 'wp-reset') . '</p>');
1912 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1913 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to delete all plugins?" data-btn-confirm="Delete plugins" data-text-wait="Deleting plugins. Please wait." data-text-confirm="All plugins except WP Reset will be deleted. There is NO UNDO. WP Reset does not make any file backups." data-text-done="%n plugins have been deleted." data-text-done-singular="One plugin has been deleted." class="button button-delete" href="#" id="delete-plugins">Delete plugins</a></p>';
1914 + echo '</div>';
1915 + echo '</div>'; // delete plugins
1916 +
1917 + echo '<div class="card default-collapsed">';
1918 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete MU Plugins & Drop-ins', 'wp-reset'), 'tool-delete-mu-plugins-dropins', array('collapse_button' => true, 'iot_button' => true, 'pro' => true)));
1919 + echo '<div class="card-body">';
1920 + echo '<p>MU Plugins are located in <code>/wp-content/mu-plugins/</code> and are, as the name suggests, must-use plugins that are automatically activated by WP and can\'t be deactiavated via the <a href="' . esc_url(admin_url('plugins.php?plugin_status=mustuse')) . '" target="_blank">plugins interface</a>, although if any are used, they are listed in the "Must Use" tab.<br>';
1921 + echo 'Drop-ins are pieces of code found in <code>/wp-content/</code> that replace default, built-in WordPress functionality. Most often used are <code>db.php</code> and <code>advanced-cache.php</code> that implement custom DB and cache functionality. They can\'t be deactivated via the <a href="' . esc_url(admin_url('plugins.php?plugin_status=dropins')) . '" target="_blank">plugins interface</a> but if any are present are listed in the "Drop-in" tab.</p>';
1922 +
1923 + if (is_multisite()) {
1924 + echo '<p class="mb0 wpmu-error">This tool is <b>not compatible</b> with WP multisite (WPMU). Using it would delete plugins for all sites in the network since they all share the same plugin files.</p>';
1092 1925 } else {
1093 - $tbl_custom++;
1926 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false, true));
1927 + echo '<p class="mb0"><a class="button button-delete button-pro-feature" href="#">Delete must use plugins - <span data-feature="tool-delete-mu-plugins" class="pro-feature"><span class="pro">PRO</span> tool</span></a><a class="button button-delete button-pro-feature" href="#">Delete drop-ins - <span data-feature="tool-delete-dropins" class="pro-feature"><span class="pro">PRO</span> tool</span></a></p>';
1094 1928 }
1095 - } // foreach
1929 + echo '</div>';
1930 + echo '</div>'; // delete MU plugins and dropins
1096 1931
1097 - echo '<p><b>Currently used WordPress tables</b>, prefixed with <i>' . $wpdb->prefix . '</i>, consist of ' . $tbl_core . ' standard and ';
1098 - if ($tbl_custom) {
1099 - echo $tbl_custom . ' custom table' . ($tbl_custom == 1? '': 's');
1100 - } else {
1101 - echo 'no custom tables';
1102 - }
1103 - echo ' totaling ' . $this->format_size($tbl_size) .' in ' . number_format($tbl_rows) . ' rows.</p>';
1104 - }
1932 + $upload_dir = wp_upload_dir(gmdate('Y/m'), true);
1933 + $upload_dir['basedir'] = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $upload_dir['basedir']);
1105 1934
1106 - echo '';
1107 - echo '</div>';
1935 + echo '<div class="card">';
1936 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Clean uploads Folder', 'wp-reset'), 'tool-delete-uploads', array('iot_button' => true, 'collapse_button' => true)));
1937 + echo '<div class="card-body">';
1938 + /* translators: %1$s is replaced with the uploads folder path, %2$s is replaced with the URL to WordPress Media upload page */
1939 + WP_Reset_Utility::wp_kses_wf('<p>' . sprintf(__('All files in <code>%1$s</code> folder will be deleted. Including folders and subfolders, and files in subfolders. Files associated with <a href="%2$s">media</a> entries will be deleted too.<br><b>There is NO UNDO. WP Reset does not make any file backups.</b>', 'wp-reset'), esc_html($upload_dir['basedir']), esc_url(admin_url('upload.php'))) . '</p>');
1940 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false));
1941 + if (false != $upload_dir['error']) {
1942 + echo '<p class="mb0"><span style="color:#dd3036;"><b>Tool is not available.</b></span> Folder is not writeable by WordPress. Please check file and folder access rights.</p>';
1943 + } else {
1944 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to delete all files &amp; folders in uploads folder?" data-btn-confirm="Delete everything in uploads folder" data-text-wait="Deleting uploads. Please wait." data-text-confirm="All files and folders in uploads will be deleted. There is NO UNDO. WP Reset does not make any file backups." data-text-done="%n files &amp; folders have been deleted." data-text-done-singular="One file or folder has been deleted." class="button button-delete" href="#" id="delete-uploads">Delete all files &amp; folders in uploads folder</a></p>';
1945 + }
1946 + echo '</div>';
1947 + echo '</div>'; // clean uploads folder
1108 1948
1109 - echo '<div class="card no-padding-bottom">';
1110 - echo '<a id="create-new-snapshot-primary" data-msg-success="Snapshot created!" data-msg-wait="Creating snapshot. Please wait." data-btn-confirm="Create snapshot" data-placeholder="Snapshot name or brief description, ie: before plugin install" data-text="Enter snapshot name or brief description, up to 64 characters." data-title="Create a new snapshot" title="Create a new database snapshot" href="#" class="button button-primary create-new-snapshot create-new-snapshot-corner">' . __('Create new', 'wp-reset') . '</a>';
1111 - echo '<h2>' . __('Saved Snapshots', 'wp-reset') . '</h2>';
1949 + echo '<div class="card default-collapsed">';
1950 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Clean wp-content Folder', 'wp-reset'), 'tool-delete-wp-content', array('collapse_button' => true, 'iot_button' => true, 'pro' => true)));
1951 + echo '<div class="card-body">';
1952 + echo '<p>All folders and their content in <code>wp-content</code> folder except the following ones will be deleted: <code>mu-plugins</code>, <code>plugins</code>, <code>themes</code>, <code>uploads</code>, <code>wp-reset-autosnapshots</code>, <code>wp-reset-snapshots-export</code>.</p>';
1953 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false));
1954 + if (false === $wp_filesystem->is_writable(trailingslashit(WP_CONTENT_DIR))) {
1955 + echo '<p class="mb0"><span style="color:#dd3036;"><b>Tool is not available.</b></span> Folder is not writeable by WordPress. Please check file and folder access rights.</p>';
1956 + } else {
1957 + echo '<p class="mb0"><a class="button button-delete button-pro-feature" href="#">Clean wp-content folder - <span data-feature="tool-delete-wp-content" class="pro-feature"><span class="pro">PRO</span> tool</span></a></p>';
1958 + }
1959 + echo '</div>';
1960 + echo '</div>'; // clean wp-content
1112 1961
1113 - if ($snapshots = $this->get_snapshots()) {
1114 - echo '<table id="wpr-snapshots">';
1115 - echo '<tr><th>Name</th><th>Info &amp; Size</th><th class="ss-actions">Actions</th></tr>';
1116 - foreach ($snapshots as $ss) {
1117 - echo '<tr id="wpr-ss-' . $ss['uid'] . '">';
1118 - if (!empty($ss['name'])) {
1119 - echo '<td title="Created on ' . date(get_option('date_format'), strtotime($ss['timestamp'])) . ' @ ' . date(get_option('time_format'), strtotime($ss['timestamp'])) . '">' . $ss['name'] . '</td>';
1120 - $name = $ss['name'];
1962 + $custom_tables = $this->get_custom_tables();
1963 +
1964 + echo '<div class="card">';
1965 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Empty or Delete Custom Tables', 'wp-reset'), 'tool-empty-delete-custom-tables', array('iot_button' => true, 'collapse_button' => true)));
1966 + echo '<div class="card-body">';
1967 + /* translators: %s is replaced with the table prefix */
1968 + WP_Reset_Utility::wp_kses_wf('<p>' . sprintf(__('This action affects only custom tables with <code>%s</code> prefix. Core WP tables and other tables in the database that do not have that prefix will not be deleted/emptied. Deleting (dropping) tables completely removes them from the database. Emptying (truncating) removes all content from them, but keeps the structure intact.<br>Always <a href="#" class="create-new-snapshot" data-description="Before deleting custom tables">create a snapshot</a> before using this tool if you want to be able to undo its actions.</p>', 'wp-reset'), esc_html($wpdb->prefix)));
1969 + if ($custom_tables) {
1970 + /* translators: %d is replaced with the number of custom tables */
1971 + WP_Reset_Utility::wp_kses_wf('<p>' . sprintf(__('The following %d custom tables are affected by this tool: ', 'wp-reset'), sizeof($custom_tables)));
1972 + foreach ($custom_tables as $tbl) {
1973 + echo '<code>' . esc_html($tbl['name']) . '</code>';
1974 + if (next($custom_tables)) {
1975 + echo ', ';
1976 + }
1977 + } // foreach
1978 + echo '.</p>';
1979 + $custom_tables_btns = '';
1121 1980 } else {
1122 - echo '<td title="Created on ' . date(get_option('date_format'), strtotime($ss['timestamp'])) . ' @ ' . date(get_option('time_format'), strtotime($ss['timestamp'])) . '">' . '' . date(get_option('date_format'), strtotime($ss['timestamp'])) . '<br>@ ' . date(get_option('time_format'), strtotime($ss['timestamp'])) . '</td>';
1123 - $name = 'created on ' . date(get_option('date_format'), strtotime($ss['timestamp'])) . ' @ ' . date(get_option('time_format'), strtotime($ss['timestamp']));
1981 + echo '<p>' . esc_html__('There are no custom tables. There\'s nothing for this tool to empty or delete.', 'wp-reset') . '</p>';
1982 + $custom_tables_btns = ' disabled';
1124 1983 }
1125 - echo '<td>' . $ss['tbl_core'] . ' standard &amp; ';
1126 - if ($ss['tbl_custom']) {
1127 - echo $ss['tbl_custom'] . ' custom table' . ($ss['tbl_custom'] == 1? '': 's');
1984 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true, true));
1985 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to empty all custom tables?" data-btn-confirm="Empty custom tables" data-text-wait="Emptying custom tables. Please wait." data-text-confirm="All custom tables with prefix <code>' . esc_attr($wpdb->prefix) . '</code> will be emptied. Always ' . esc_attr('<a href="#" class="create-new-snapshot" data-description="Before emptying custom tables">create a snapshot</a> if you want to be able to undo') . '." data-text-done="%n custom tables have been emptied." data-text-done-singular="One custom table has been emptied." class="button button-delete' . esc_attr($custom_tables_btns) . '" href="#" id="truncate-custom-tables">Empty (truncate) custom tables</a>';
1986 + echo '<a data-confirm-title="Are you sure you want to delete all custom tables?" data-btn-confirm="Delete custom tables" data-text-wait="Deleting custom tables. Please wait." data-text-confirm="All custom tables with prefix <code>' . esc_attr($wpdb->prefix) . '</code> will be deleted. Always ' . esc_attr('<a href="#" class="create-new-snapshot" data-description="Before deleting custom tables">create a snapshot</a> if you want to be able to undo') . '." data-text-done="%n custom tables have been deleted." data-text-done-singular="One custom table has been deleted." class="button button-delete' . esc_attr($custom_tables_btns) . '" href="#" id="drop-custom-tables">Delete (drop) custom tables</a>';
1987 + WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('drop-custom-tables', 'Before deleting custom tables'));
1988 + echo '</p>';
1989 + echo '</div>';
1990 + echo '</div>'; // empty custom tables
1991 +
1992 + echo '<div class="card default-collapsed">';
1993 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Switch WP Version', 'wp-reset'), 'tool-switch-wp-version', array('collapse_button' => true, 'iot_button' => true, 'pro' => true)));
1994 + echo '<div class="card-body">';
1995 + if (is_multisite()) {
1996 + echo '<p class="mb0 wpmu-error">This tool is <b>not compatible</b> with WP multisite (WPMU). Using it would change the WP version for all sites in the network since they all share the same core files.</p>';
1128 1997 } else {
1129 - echo 'no custom tables';
1998 + echo '<p>Replace current WordPress version with the selected new version. Switching from a previous version, to a newer version is mostly supported and properly handled by the WP installer. Reverting WordPress, rolling back WordPress to a previous version is not supported. Results may vary!</p>';
1999 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
2000 +
2001 + $wp_versions = WP_Reset_Utility::get_wordpress_versions();
2002 + echo '<p><label for="select-wp-version">Select the WordPress version to switch to:</label> ';
2003 + echo '<select id="select-wp-version">';
2004 + echo '<option value="">select WordPress version</option>';
2005 + foreach ($wp_versions as $version => $release_date) {
2006 + if ($release_date == 'bleeding') {
2007 + echo '<option value="bleeding">WordPress v' . esc_html($version) . ' (Bleeding edge nightly)' . ($wp_version == $version ? ' - installed' : '') . '</option>';
2008 + } elseif ($release_date == 'point') {
2009 + echo '<option value="point-' . esc_attr(substr($version, 0, 3)) . '">WordPress v' . esc_html($version) . ' (Point release nightly)' . ($wp_version == $version ? ' - installed' : '') . '</option>';
2010 + } else {
2011 + echo '<option value="' . esc_attr($version) . '">WordPress v' . esc_html($version) . ' (' . esc_attr(gmdate('Y-m-d', $release_date)) . ')' . ($wp_version == $version ? ' - installed' : '') . '</option>';
2012 + }
2013 + }
2014 + echo '</select></p>';
2015 +
2016 + echo '<p class="mb0">';
2017 + echo '<a class="button button-delete button-pro-feature" href="#">Switch WordPress version - <span data-feature="tool-switch-wp-version" class="pro-feature"><span class="pro">PRO</span> tool</span></a>';
2018 + echo '</p>';
1130 2019 }
1131 - echo ' totaling ' . $this->format_size($ss['tbl_size']) . ' in ' . number_format($ss['tbl_rows']) . ' rows</td>';
1132 - echo '<td>';
1133 - echo '<a data-title="Current DB tables compared to snapshot %s" data-wait-msg="Comparing. Please wait." data-name="' . $name . '" title="Compare snapshot to current database tables" href="#" class="ss-action compare-snapshot" data-ss-uid="' . $ss['uid'] . '"><span class="dashicons dashicons-visibility"></span></a>';
1134 - echo '<a data-btn-confirm="Restore snapshot" data-text-wait="Restoring snapshot. Please wait." data-text-confirm="Are you sure you want to restore the selected snapshot? There is NO UNDO.<br>Restoring the snapshot will delete all current standard and custom tables and replace them with tables from the snapshot." data-text-done="Snapshot has been restored. Click OK to reload the page with new data." title="Restore snapshot by overwriting current database tables" href="#" class="ss-action restore-snapshot" data-ss-uid="' . $ss['uid'] . '"><span class="dashicons dashicons-backup"></span></a>';
1135 - echo '<a data-success-msg="Snapshot export created!<br><a href=\'%s\'>Download it</a>" data-wait-msg="Exporting snapshot. Please wait." title="Download snapshot as gzipped SQL dump" href="#" class="ss-action download-snapshot" data-ss-uid="' . $ss['uid'] . '"><span class="dashicons dashicons-download"></span></a>';
1136 - echo '<a data-btn-confirm="Delete snapshot" data-text-wait="Deleting snapshot. Please wait." data-text-confirm="Are you sure you want to delete the selected snapshot and all its data? There is NO UNDO.<br>Deleting the snapshot will not affect the active database tables in any way." data-text-done="Snapshot has been deleted." title="Permanently delete snapshot" href="#" class="ss-action delete-snapshot" data-ss-uid="' . $ss['uid'] . '"><span class="dashicons dashicons-trash"></span></a></td>';
1137 - echo '</tr>';
1138 - } // foreach
1139 - echo '</table>';
1140 - echo '<p id="ss-no-snapshots" class="hidden">There are no saved snapshots. <a href="#" class="create-new-snapshot">Create a new snapshot.</a></p>';
1141 - } else {
1142 - echo '<p id="ss-no-snapshots">There are no saved snapshots. <a href="#" class="create-new-snapshot">Create a new snapshot.</a></p>';
1143 - }
2020 + echo '</div>';
2021 + echo '</div>'; // switch WP version
1144 2022
1145 - echo '</div>';
1146 - } // tab_snapshots
2023 + echo '<div class="card">';
2024 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete .htaccess File', 'wp-reset'), 'tool-delete-htaccess', array('iot_button' => true, 'collapse_button' => true)));
2025 + echo '<div class="card-body">';
2026 + /* translators: %s is replaced with the .htaccess file path */
2027 + WP_Reset_Utility::wp_kses_wf('<p>' . sprintf(__('This action deletes the .htaccess file located in <code>%s</code><br><b>There is NO UNDO. WP Reset does not make any file backups.</b></p>', 'wp-reset'), esc_html($this->get_htaccess_path())));
1147 2028
2029 + echo '<p>If you need to edit .htaccess, install our free <a href="' . esc_url(admin_url('plugin-install.php?tab=plugin-information&plugin=wp-htaccess-editor&TB_iframe=true&width=600&height=550')) . '" class="thickbox open-plugin-details-modal">WP Htaccess Editor</a> plugin. It automatically creates backups when you edit .htaccess as well as checks for syntax errors. To create the default .htaccess file open <a href="' . esc_url(admin_url('options-permalink.php')) . '">Settings - Permalinks</a> and re-save settings. WordPress will recreate the file.</p>';
2030 + WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false));
2031 + echo '<p class="mb0"><a data-confirm-title="Are you sure you want to delete the .htaccess file?" data-btn-confirm="Delete .htaccess file" data-text-wait="Deleting .htaccess file. Please wait." data-text-confirm="Htaccess file will be deleted. There is NO UNDO. WP Reset does not make any file backups." data-text-done="Htaccess file has been deleted." data-text-done-singular="Htaccess file has been deleted." class="button button-delete" href="#" id="delete-htaccess">Delete .htaccess file</a></p>';
1148 2032
1149 - /**
1150 - * Echoes content for sponsor tab
1151 - *
1152 - * @return null
1153 - */
1154 - private function tab_geoip() {
1155 - echo '<div class="card">';
1156 - echo '<h2>' . __('WP Reset is proudly sponsored by IP Geolocation', 'wp-reset') . '</h2>';
1157 - echo '<p>' . __('Keeping a plugin maintained, supported and free is neither easy nor cheap that\'s why we\'re thrilled that a <a href="https://ipgeolocation.io/" target="_blank">premium GeoIP service</a> decided to sponsor WP Reset. No notifications, no popups, no shady links. They keep the plugin free and clean.', 'wp-reset') . '</p>';
1158 - echo '</div>';
2033 + echo '</div>';
2034 + echo '</div>'; // delete htaccess
2035 + } // tab_tools
1159 2036
1160 - echo '<div class="card">';
1161 - echo '<h2>' . __('Why would I need a GeoIP service?', 'wp-reset') . '</h2>';
1162 - echo '<p>' . __('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 <a href="https://wpreset.com/geoip-transform-boring-data-better-user-experience/" target="_blank">example</a> we recently wrote about.', 'wp-reset') . '</p>';
1163 - echo '<p><a href="https://ipgeolocation.io/ip-location" target="_blank" class="button">See what data is available for your IP address</a></p>';
1164 - echo '</div>';
1165 2037
1166 - echo '<div class="card">';
1167 - echo '<h2>' . __('Get a free account', 'wp-reset') . '</h2>';
1168 - echo '<p>' . __('IP Geolocation knows how difficult it is to start any new project. That\'s why they offer <a href="https://ipgeolocation.io/signup" target="_blank">50,000 API requests per month for free</a>. 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') . '</p>';
1169 - echo '<p><a href="https://ipgeolocation.io/signup" target="_blank" class="button">Get a FREE account with 50,000 API requests a month</a></p>';
1170 - echo '</div>';
2038 + /**
2039 + * Echoes content for collections tab
2040 + *
2041 + * @return null
2042 + */
2043 + private function tab_collections()
2044 + {
2045 + echo '<div class="card">';
2046 + WP_Reset_Utility::wp_kses_wf($this->get_card_header('What are Plugin & Theme Collections?', 'collections-info', array('collapse_button' => false)));
2047 + echo '<div class="card-body">';
2048 + WP_Reset_Utility::wp_kses_wf('<p>' . __('Have a set of plugins (and themes) that you install and activate after every reset? Or on every fresh WordPress installation? Well, no more clicking install &amp; active for ten minutes! Build the collection once and install it with one click as many times as needed.</p><p>WP Reset stores collections in the cloud so they\'re accessible on every site you build. You can use free plugins and themes from the official repo, and PRO ones by uploading a ZIP file. We\'ll safely store your license keys too, so you have everything in one place.', 'wp-reset') . '</p>');
2049 + echo '<p><a class="button button-secondary button-pro-feature" href="#">Add a new collection - <span data-feature="collections" class="pro-feature"><span class="pro">PRO</span> feature</span></a> &nbsp; <a class="button button-secondary button-pro-feature" href="#">Reload my saved collections from the cloud - <span data-feature="collections" class="pro-feature"><span class="pro">PRO</span> feature</span></a></p>';
2050 + echo '</div>';
2051 + echo '</div>'; // collections-info
1171 2052
1172 - echo '<p>Permanently <a href="#" class="wpr-dismiss-notice" data-notice="geoip_tab">remove this tab</a>.</p>';
1173 - } // tab_geoip
2053 + $plugins = array();
2054 + $plugins['eps-301-redirects'] = array('name' => '301 Redirects', 'desc' => 'Easiest way to manage redirects');
2055 + $plugins['classic-editor'] = array('name' => 'Classic Editor', 'desc' => 'Any easy fix for all your Gutenberg caused troubles');
2056 + $plugins['simple-author-box'] = array('name' => 'Simple Author Box', 'desc' => 'Simplest way to add responsive, great looking author boxes');
2057 + $plugins['sticky-menu-or-anything-on-scroll'] = array('name' => 'Sticky Menu (or Anything!) on Scroll', 'desc' => 'Make any element on the page sticky.');
2058 + $plugins['under-construction-page'] = array('name' => 'UnderConstructionPage', 'desc' => 'Working on your site? Put it in the under construction mode.');
2059 + $plugins['wp-external-links'] = array('name' => 'WP External Links', 'desc' => 'Manage all external & internal links. Control icons, nofollow, noopener, UGC, sponsored and if links open in new window or new tab.');
1174 2060
2061 + echo '<div class="card" data-collection-id="1">';
2062 + WP_Reset_Utility::wp_kses_wf($this->get_card_header('Must Have WordPress Plugins', 'collection-id-1', array('collapse_button' => false)));
2063 + echo '<div class="card-body"><div class="thirdx2"><p class="_mb0"></p><div class="dropdown dropdown-right">
2064 + <a class="button dropdown-toggle" href="#">Install collection</a>
2065 + <div class="dropdown-menu">
2066 + <a class="dropdown-item install-collection" data-activate="true" href="#">Install &amp; activate collection</a>
2067 + <a class="dropdown-item install-collection" href="#">Install collection</a>
2068 + <a data-feature="collections" class="dropdown-item button-pro-feature" href="#">Delete installed plugins &amp; themes then install &amp; activate collection - <span class="pro-feature" data-feature="cloud-wpr"><span class="pro">PRO</span> Feature</span></a>
2069 + <a data-feature="collections" class="dropdown-item button-pro-feature" href="#">Delete installed plugins &amp; themes then install collection - <span class="pro-feature" data-feature="cloud-wpr"><span class="pro">PRO</span> Feature</span></a>
2070 + </div>
2071 + </div><a class="button add-collection-item button-pro-feature" href="#">Add new plugin or theme - <span data-feature="collections" class="pro-feature"><span class="pro">PRO</span> feature</span></a></div><div class="third textright"><p class="_mb0"></p><div class="dropdown">
2072 + <a class="button dropdown-toggle" href="#">Actions</a>
2073 + <div class="dropdown-menu">
2074 + <a class="dropdown-item button-pro-feature" href="#">Add new collection - <span class="pro-feature" data-feature="collections"><span class="pro">PRO</span> Feature</span></a>
2075 + <a class="dropdown-item button-pro-feature" href="#">Rename collection - <span class="pro-feature" data-feature="collections"><span class="pro">PRO</span> Feature</span></a>
2076 + <a class="dropdown-item button-delete button-pro-feature" href="#">Delete collection - <span class="pro-feature" data-feature="collections"><span class="pro">PRO</span> Feature</span></a>
2077 + </div>
2078 + </div><p></p></div><table class="collection-table"><tbody><tr><th>Type</th><th>Name &amp; Note</th><th class="actions">Actions</th></tr>';
2079 + foreach ($plugins as $slug => $plugin) {
2080 + echo '<tr data-slug="' . esc_attr($slug) . '"><td><span class="dashicons dashicons-admin-plugins tooltip" title="Plugin"></span><span class="dashicons dashicons-wordpress tooltip" title="Comes from the WordPress repository"></span></td><td class="collection-item-details"><span>' . esc_html($plugin['name']) . '</span><i>' . esc_html($plugin['desc']) . '</i></td><td class="textcenter"><div class="dropdown">
2081 + <a class="button dropdown-toggle" href="#">Actions</a>
2082 + <div class="dropdown-menu">
2083 + <a href="#" class="dropdown-item install-collection-item button-pro-feature">Install - <span class="pro-feature" data-feature="collections"><span class="pro">PRO</span> Feature</span></a>
2084 + <a href="#" class="dropdown-item install-collection-item button-pro-feature">Install &amp; Activate - <span class="pro-feature" data-feature="collections"><span class="pro">PRO</span> Feature</span></a>
2085 + <a href="#" class="dropdown-item edit-collection-item button-pro-feature">Edit - <span class="pro-feature" data-feature="collections"><span class="pro">PRO</span> Feature</span></a>
2086 + <a href="#" class="dropdown-item button-delete button-link-delete delete-collection-item button-pro-feature">Delete - <span class="pro-feature" data-feature="collections"><span class="pro">PRO</span> Feature</span></a>
2087 + </div>
2088 + </div></td></tr>';
2089 + } // foreach plugin
2090 + echo '</tbody></table></div></div>';
2091 + } // tab_collections
1175 2092
1176 - /**
1177 - * Helper function for generating UTM tagged links
1178 - *
1179 - * @param string $placement Optional. UTM content param.
1180 - * @param string $page Optional. Page to link to.
1181 - * @param array $params Optional. Extra URL params.
1182 - * @param string $anchor Optional. URL anchor part.
1183 - *
1184 - * @return string
1185 - */
1186 - function generate_web_link($placement = '', $page = '/', $params = array(), $anchor = '') {
1187 - $base_url = 'https://wpreset.com';
1188 2093
1189 - if ('/' != $page) {
1190 - $page = '/' . trim($page, '/') . '/';
1191 - }
1192 - if ($page == '//') {
1193 - $page = '/';
1194 - }
2094 + /**
2095 + * Echoes content for support tab
2096 + *
2097 + * @return null
2098 + */
2099 + private function tab_support()
2100 + {
2101 + echo '<div class="card">';
2102 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Documentation', 'wp-reset'), 'support-documentation', array('collapse_button' => false)));
2103 + echo '<div class="card-body">';
2104 + /* translators: %s is replaced with the link to the documentation page */
2105 + WP_Reset_Utility::wp_kses_wf('<p class="mb0">' . sprintf(__('All tools and features are explained in detail in <a href="%s" target="_blank">the documentation</a>. We did our best to describe how things work on both the code level and an "average user" level.', 'wp-reset'), esc_url($this->generate_web_link('support-tab', '/documentation/'))) . '</p>');
2106 + echo '</div>';
2107 + echo '</div>'; // documentation
1195 2108
1196 - $parts = array_merge(array('utm_source' => 'wp-reset-free', 'utm_medium' => 'plugin', 'utm_content' => $placement, 'utm_campaign' => 'wp-reset-free-v' . $this->version), $params);
2109 + echo '<div class="card">';
2110 + WP_Reset_Utility::wp_kses_wf($this->get_card_header('Emergency Recovery Script', 'support-ers', array('collapse_button' => false, 'pro' => true)));
2111 + echo '<div class="card-body">';
2112 + echo '<p>Emergency Recovery Script is a standalone, single-file, WordPress independent PHP script created to <b>recover WordPress sites from the most difficult situations</b>. When access to the admin is not possible when core files are compromised (accidental delete or malware related situations), when you get the white screen of death, can\'t log in for whatever reason or a plugin has killed your site - emergency recovery script can fix the problem! Some of the things ERS can do;</p>';
2113 + echo '<ul class="plain-list">';
2114 + echo '<li>Test the integrity of all WP core files and reinstall them if needed</li>';
2115 + echo '<li>Detect and remove all files in core folders that are not a part of WP</li>';
2116 + echo '<li>Deactivate and activate plugins without logging in to WP admin</li>';
2117 + echo '<li>Deactivate and activate themes without logging in to WP admin</li>';
2118 + echo '<li>Reset user privileges and roles</li>';
2119 + echo '<li>Create new WP admin accounts without logging in to WP admin or knowing the admin username/password</li>';
2120 + echo '<li>Modify WordPress address and site address</li>';
2121 + echo '</ul>';
2122 + echo '<p class="mb0">You can install the script as a preventive measure, so it\'s always available in case of an emergency (don\'t worry, it\'s password protected), or upload it only when needed. On production sites, when big and potentially dangerous changes rarely happen, we suggest uploading it only when needed. On test sites, have it ready in advance because there\'s a higher probability that you\'ll need it. Emergency Recovery Script is a <span class="pro-feature pro-feature-text" data-feature="support-ers">WP Reset <span>PRO</span></span> tool.</p>';
2123 + echo '</div>';
2124 + echo '</div>'; // emergency recovery script
1197 2125
1198 - if (!empty($anchor)) {
1199 - $anchor = '#' . trim($anchor, '#');
1200 - }
2126 + echo '<div class="card">';
2127 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Public Support Forum', 'wp-reset'), 'support-forum', array('collapse_button' => false)));
2128 + echo '<div class="card-body">';
2129 + WP_Reset_Utility::wp_kses_wf('<p>' . __('We are very active on the <a href="https://wordpress.org/support/plugin/wp-reset" target="_blank">official WP Reset support forum</a>. 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') . '</p>');
2130 + echo '</div>';
2131 + echo '</div>'; // forum
1201 2132
1202 - $out = $base_url . $page . '?' . http_build_query($parts, '', '&amp;') . $anchor;
2133 + echo '<div class="card">';
2134 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Premium Email Support', 'wp-reset'), 'support-email', array('collapse_button' => false, 'pro' => true)));
2135 + echo '<div class="card-body">';
2136 + echo '<p class="mb0">Need urgent support? Have one of our devs personally help you with your issue. All PRO license holders have access to premium email support. Get <span class="pro-feature pro-feature-text" data-feature="support-email">WP Reset <span>PRO</span></span> now.</p>';
2137 + echo '</div>';
2138 + echo '</div>'; // email support
1203 2139
1204 - return $out;
1205 - } // generate_web_link
2140 + echo '<div class="card">';
2141 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Care to Help Out?', 'wp-reset'), 'support-help-out', array('collapse_button' => false)));
2142 + echo '<div class="card-body">';
2143 + WP_Reset_Utility::wp_kses_wf('<p class="mb0">' . __('No need for donations :) If you can give us a <a href="https://wordpress.org/support/plugin/wp-reset/reviews/#new-post" target="_blank">five star rating</a> you\'ll help out more than you can imagine. A public mention <a href="https://twitter.com/webfactoryltd" target="_blank">@webfactoryltd</a> also does wonders. Thank you!', 'wp-reset') . '</p>');
2144 + echo '</div>';
2145 + echo '</div>'; // help out
2146 + } // tab_support
1206 2147
1207 2148
1208 - /**
1209 - * Returns all saved snapshots from DB
1210 - *
1211 - * @return array
1212 - */
1213 - function get_snapshots() {
1214 - $snapshots = get_option('wp-reset-snapshots', array());
2149 + /**
2150 + * Echoes content for pro tab
2151 + *
2152 + * @return null
2153 + */
2154 + private function tab_pro()
2155 + {
2156 + $agency_lifetime = $this->generate_web_link('pricing-table', '/buy/', array('p' => 'wp-reset-pro-agency-ltd-launch'));
2157 + $team_lifetime = $this->generate_web_link('pricing-table', '/', array(), 'pricing');
2158 + $personal_lifetime = $this->generate_web_link('pricing-table', '/buy/', array('p' => 'wp-reset-pro-personal-ltd-launch'));
1215 2159
1216 - return $snapshots;
1217 - } // get_snapshots
2160 + echo '<div class="card">';
2161 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('WP Reset PRO', 'wp-reset'), 'pro-features', array('collapse_button' => false)));
2162 + echo '<div class="card-body">';
2163 + echo '<p>More ways to reset your site, more tools, automatic snapshots, collections, email support and the emergency recover script - that\'s WP Reset PRO in a nutshell. The same <b>quality and easy-of-use</b> you experienced in the free version is very much a part of the PRO one, but extended and upgraded with more tools that will save you even more time.</p>';
2164 + echo '<p>WP Reset PRO is aimed towards <b>webmasters, agencies, and everyone who buildsa a lot of WordPress sites</b>. It\'s much, much more than a "reset" tool. It\'s an easy way to start a new site, to test changes and to get out of the thickest jams. And thanks to its cloud features and the Dashboard it\'ll give you access to collections and snapshots on all the sites you\'re working on - instantly, without dragging any files along.</p>';
2165 + echo '<p>Give WP Reset PRO a go. <b>It\'ll pay itself out in hours saved within the first few days!</b></p>';
2166 + echo '<p>If you already have a PRO license, activate it below.</p>';
2167 + echo '<p class="textcenter"><a href="#" data-feature="purchase-pro" class="button-pro-feature button button-delete">Get PRO now</a></p>';
2168 + echo '</div>';
2169 + echo '</div>';
1218 2170
2171 + echo '<div class="card">';
2172 + WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Activate PRO License', 'wp-reset'), 'pro-activate', array('collapse_button' => false)));
2173 + echo '<div class="card-body">';
1219 2174
1220 - /**
1221 - * Returns all custom table names, with prefix
1222 - *
1223 - * @return array
1224 - */
1225 - function get_custom_tables() {
1226 - global $wpdb;
1227 - $custom_tables = array();
2175 + echo '<p>License key is visible on the confirmation screen, right after purchasing. You can also find it in the confirmation email sent to the email address provided on purchase. Or use keys created with the <a href="https://dashboard.wpreset.com/licenses/" target="_blank">license manager</a>.</p>
2176 + <p>If you don\'t have a license - <a class="button-pro-feature" href="#" data-feature="purchase-pro2">purchase one now</a>. In case of problems with the license please <a href="' . esc_url($this->generate_web_link('pro-tab-license', '/contact/')) . '" target="_blank">contact support</a>.</p>';
1228 2177
1229 - $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1230 - if (is_array($table_status)) {
1231 - foreach ($table_status as $index => $table) {
1232 - if (0 !== stripos($table->Name, $wpdb->prefix)) {
1233 - continue;
2178 + echo '<hr>';
2179 + echo '<p><label for="wpr-license-key">License Key: </label><input class="regular-text" type="text" id="wpr-license-key" value="' . ($this->license->get_license('license_key') != 'keyless' ? esc_attr($this->license->get_license('license_key')) : '') . '" placeholder="12345678-12345678-12345678-12345678">';
2180 +
2181 + echo '<br><label>Status: </label>';
2182 + if ($this->license->is_active()) {
2183 + $license_formatted = $this->license->get_license_formatted();
2184 + echo '<b style="color: #66b317;">Active</b><br>
2185 + <label>Type: </label>' . esc_html($license_formatted['name_long']);
2186 + echo '<br><label>Valid: </label>' . esc_html($license_formatted['valid_until']);
2187 +
2188 + echo '<p>Thank you for purchasing WP Reset PRO! <b>Your license has been verified and activated.</b>';
2189 + echo '<br>To start using the PRO version, please follow these steps:</p>';
2190 + echo '<ol>';
2191 + echo '<li><a href="https://dashboard.wpreset.com/pro-download/" target="_blank">Download</a> the latest version of the PRO plugin.</li>';
2192 + echo '<li>Go to <a href="' . esc_url(admin_url('plugin-install.php')) . '">Plugins - Add New - Upload Plugin</a> and upload the ZIP you just downloaded.</li>';
2193 + echo '<li>If asked to replace (overwrite) the free version - confirm it.</li>';
2194 + echo '<li>Activate the plugin.</li>';
2195 + echo '<li>That\'s it, no more steps.</li>';
2196 + echo '</ol>';
2197 + } else { // not active
2198 + echo '<strong style="color: #ea1919;">Inactive</strong>';
2199 + if (!empty($this->license->get_license('error'))) {
2200 + echo '<br><label>Error: </label>' . esc_html($this->license->get_license('error'));
2201 + }
1234 2202 }
1235 - if (empty($table->Engine)) {
1236 - continue;
2203 + echo '</p>';
2204 +
2205 + echo '<p>';
2206 + if ($this->license->is_active()) {
2207 + echo '<a href="#" id="wpr-save-license" data-text-wait="Validating. Please wait." class="button button-secondary">Save &amp; Revalidate License</a>';
2208 + echo '&nbsp; &nbsp;<a href="#" id="wpr-deactivate-license" data-text-wait="Deactivating. Please wait." class="button button-delete">Deactivate License</a>';
2209 + } else {
2210 + echo '<a href="#" id="wpr-save-license" data-text-wait="Activating. Please wait." class="button button-primary">Save &amp; Activate License</a>';
2211 + echo '&nbsp; &nbsp;<a href="#" data-text-wait="Activating. Please wait." class="button button-secondary" id="wpr-keyless-activation">Keyless Activation</a>';
1237 2212 }
2213 + echo '</p>';
2214 + echo '<p class="mb0"><i>By attempting to activate a license you agree to share the following data with <a target="_blank" href="https://www.webfactoryltd.com/">WebFactory Ltd</a>: license key, site URL, site title, site WP version, site IP, and WP Reset plugin (free) version.</i>';
2215 + echo '</p>';
1238 2216
1239 - if (false === in_array($table->Name, $this->core_tables)) {
1240 - $custom_tables[] = array('name' => $table->Name, 'rows' => $table->Rows, 'data_length' => $table->Data_length, 'index_length' => $table->Index_length);
2217 + echo '</div>';
2218 + echo '</div>'; // activate PRO
2219 +
2220 + WP_Reset_Utility::wp_kses_wf($this->pro_dialog());
2221 + } // tab_pro
2222 +
2223 +
2224 + function pro_dialog()
2225 + {
2226 + $out = '';
2227 +
2228 + $out .= '<div id="wpreset-pro-dialog" style="display: none;" title="WP Reset PRO"><span class="ui-helper-hidden-accessible"><input type="text"/></span>';
2229 +
2230 + $out .= '<div class="center logo"><a href="https://wpreset.com/?ref=wpreset-free-pricing-table" target="_blank"><img src="' . $this->plugin_url . 'img/wp-reset-logo.png' . '" alt="WP Reset PRO" title="WP Reset PRO"></a><br>';
2231 +
2232 + $out .= '<span>Limited PRO Offer - <b>all prices are LIFETIME</b>! Pay once &amp; use forever!</span>';
2233 + $out .= '</div>';
2234 +
2235 + $out .= '<table id="wpreset-pro-table">';
2236 + $out .= '<tr>';
2237 + $out .= '<td class="center">Lifetime Personal License</td>';
2238 + $out .= '<td class="center">Lifetime Team License</td>';
2239 + $out .= '<td class="center">Lifetime Agency License</td>';
2240 + $out .= '</tr>';
2241 +
2242 + $out .= '<tr class="prices">';
2243 + $out .= '<td class="center"><del>$39 /year</del><br><span>$59</span> <b>/lifetime</b></td>';
2244 + $out .= '<td class="center"><del>$79 /year</del><br><span>$69</span> <b>/lifetime</b></td>';
2245 + $out .= '<td class="center"><del>$119 /year</del><br><span>$149</span> <b>/lifetime</b></td>';
2246 + $out .= '</tr>';
2247 +
2248 + $out .= '<tr>';
2249 + $out .= '<td><span class="dashicons dashicons-yes"></span><b>1 Site License</b></td>';
2250 + $out .= '<td><span class="dashicons dashicons-yes"></span><b>5 Sites License</b></td>';
2251 + $out .= '<td><span class="dashicons dashicons-yes"></span><b>100 Sites License</b></td>';
2252 + $out .= '</tr>';
2253 +
2254 + $out .= '<tr>';
2255 + $out .= '<td><span class="dashicons dashicons-yes"></span>All Plugin Features &amp; Tools</td>';
2256 + $out .= '<td><span class="dashicons dashicons-yes"></span>All Plugin Features &amp; Tools</td>';
2257 + $out .= '<td><span class="dashicons dashicons-yes"></span>All Plugin Features &amp; Tools</td>';
2258 + $out .= '</tr>';
2259 +
2260 + $out .= '<tr>';
2261 + $out .= '<td><span class="dashicons dashicons-yes"></span>Lifetime Updates &amp; Support</td>';
2262 + $out .= '<td><span class="dashicons dashicons-yes"></span>Lifetime Updates &amp; Support</td>';
2263 + $out .= '<td><span class="dashicons dashicons-yes"></span>Lifetime Updates &amp; Support</td>';
2264 + $out .= '</tr>';
2265 +
2266 + $out .= '<tr>';
2267 + $out .= '<td><span class="dashicons dashicons-yes"></span>2 GB WP Reset Cloud Storage</td>';
2268 + $out .= '<td><span class="dashicons dashicons-yes"></span>10 GB WP Reset Cloud Storage</td>';
2269 + $out .= '<td><span class="dashicons dashicons-yes"></span>100 GB WP Reset Cloud Storage</td>';
2270 + $out .= '</tr>';
2271 +
2272 + $out .= '<tr>';
2273 + $out .= '<td><span class="dashicons dashicons-yes"></span>Dropbox &amp; Google Drive support</td>';
2274 + $out .= '<td><span class="dashicons dashicons-yes"></span>Dropbox &amp; Google Drive support</td>';
2275 + $out .= '<td><span class="dashicons dashicons-yes"></span>Dropbox &amp; Google Drive support</td>';
2276 + $out .= '</tr>';
2277 +
2278 + $out .= '<tr>';
2279 + $out .= '<td><span class="dashicons dashicons-yes"></span>Plugins & Themes Collections</td>';
2280 + $out .= '<td><span class="dashicons dashicons-yes"></span>Plugins & Themes Collections</td>';
2281 + $out .= '<td><span class="dashicons dashicons-yes"></span>Plugins & Themes Collections</td>';
2282 + $out .= '</tr>';
2283 +
2284 + $out .= '<tr>';
2285 + $out .= '<td><span class="dashicons dashicons-yes"></span>Automatic Snapshots</td>';
2286 + $out .= '<td><span class="dashicons dashicons-yes"></span>Automatic Snapshots</td>';
2287 + $out .= '<td><span class="dashicons dashicons-yes"></span>Automatic Snapshots</td>';
2288 + $out .= '</tr>';
2289 +
2290 + $out .= '<tr>';
2291 + $out .= '<td><span class="dashicons dashicons-yes"></span>Emergency Recovery Script</td>';
2292 + $out .= '<td><span class="dashicons dashicons-yes"></span>Emergency Recovery Script</td>';
2293 + $out .= '<td><span class="dashicons dashicons-yes"></span>Emergency Recovery Script</td>';
2294 + $out .= '</tr>';
2295 +
2296 + $out .= '<tr>';
2297 + $out .= '<td><span class="dashicons dashicons-no"></span>Licenses &amp; Sites Manager</td>';
2298 + $out .= '<td><span class="dashicons dashicons-yes"></span>Licenses &amp; Sites Manager</td>';
2299 + $out .= '<td><span class="dashicons dashicons-yes"></span>Licenses &amp; Sites Manager</td>';
2300 + $out .= '</tr>';
2301 +
2302 + $out .= '<tr>';
2303 + $out .= '<td><span class="dashicons dashicons-no"></span>White-label Mode</td>';
2304 + $out .= '<td><span class="dashicons dashicons-yes"></span>White-label Mode</td>';
2305 + $out .= '<td><span class="dashicons dashicons-yes"></span>White-label Mode</td>';
2306 + $out .= '</tr>';
2307 +
2308 + $out .= '<tr>';
2309 + $out .= '<td><span class="dashicons dashicons-no"></span>Full Plugin Rebranding</td>';
2310 + $out .= '<td><span class="dashicons dashicons-no"></span>Full Plugin Rebranding</td>';
2311 + $out .= '<td><span class="dashicons dashicons-yes"></span>Full Plugin Rebranding</td>';
2312 + $out .= '</tr>';
2313 +
2314 + $out .= '<tr>';
2315 + $out .= '<td><a class="button button-buy" data-href-org="https://wpreset.com/buy2/?product=personal-free&ref=pricing-table" href="https://wpreset.com/buy2/?product=personal-free&ref=pricing-table" target="_blank">Lifetime License<br>$59 -&gt; BUY NOW</a>
2316 + <br>or <a class="button-buy" data-href-org="https://wpreset.com/buy2/?product=personal-monthly&ref=pricing-table" href="https://wpreset.com/buy2/?product=personal-monthly&ref=pricing-table" target="_blank">only $6.99 <small>/month</small></a></td>';
2317 + $out .= '<td><a class="button button-buy" data-href-org="https://wpreset.com/buy2/?product=team-free&ref=pricing-table" href="https://wpreset.com/buy2/?product=team-free&ref=pricing-table" target="_blank">Lifetime License<br>$69 -&gt; BUY NOW</a></td>';
2318 + $out .= '<td><a class="button button-buy" data-href-org="https://wpreset.com/buy2/?product=agency-free&ref=pricing-table" href="https://wpreset.com/buy2/?product=agency-free&ref=pricing-table" target="_blank">Lifetime License<br>$149 -&gt; BUY NOW</a></td>';
2319 + $out .= '</tr>';
2320 +
2321 + $out .= '</table>';
2322 +
2323 + $out .= '<div class="center footer"><b>100% No-Risk Money Back Guarantee!</b> If you don\'t like the plugin over the next 7 days, we will happily refund 100% of your money. No questions asked! Payments are processed by our merchant of records - <a href="https://paddle.com/" target="_blank">Paddle</a>.</div></div>';
2324 +
2325 + return $out;
2326 + } // pro_dialog
2327 +
2328 +
2329 + /**
2330 + * Echoes content for snapshots tab
2331 + *
2332 + * @return null
2333 + */
2334 + private function tab_snapshots()
2335 + {
2336 + global $wpdb;
2337 + $tbl_core = $tbl_custom = $tbl_size = $tbl_rows = 0;
2338 +
2339 + echo '<div class="card" id="card-snapshots">';
2340 + echo '<h4>';
2341 + echo esc_html__('Snapshots', 'wp-reset');
2342 + echo '<div class="card-header-right"><a class="toggle-card tooltip" href="#" title="' . esc_html__('Collapse / expand box', 'wp-reset') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a></div>';
2343 + echo '</h4>';
2344 + echo '<div class="card-body">';
2345 + echo '<p>A snapshot is a copy of all WP database tables, standard and custom ones, saved in the site\'s database. <a href="https://www.youtube.com/watch?v=xBfMmS12vMY" target="_blank">Watch a short video</a> overview and tutorial about Snapshots.</p>';
2346 +
2347 + echo '<p>Snapshots are primarily a development tool. When using various reset tools we advise using our 1-click snapshot tool available in every tool\'s confirmation dialog. If a full backup that includes files is needed, use one of the <a href="' . esc_url(admin_url('plugin-install.php?s=backup&tab=search&type=term')) . '" target="_blank">backup plugins</a> from the repo.</p>';
2348 +
2349 + echo '<p>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.</p>';
2350 +
2351 + echo '<p>To automatically generate snapshots on plugin, theme, and core update, activate, deactivate and similar events enable automatic snapshots available in <span class="pro-feature pro-feature-text" data-feature="snapshots-auto">WP Reset <span>PRO</span></span>.</p>';
2352 +
2353 + // phpcs:ignore db call warning as we are using uncommon queries
2354 + $tables = $wpdb->get_results('SHOW TABLES', ARRAY_N); // phpcs:ignore
2355 + if (is_array($tables)) {
2356 + foreach ($tables as $table) {
2357 + if (0 !== stripos($table[0], $wpdb->prefix)) {
2358 + continue;
2359 + }
2360 +
2361 + if (in_array($table[0], $this->core_tables)) {
2362 + $tbl_core++;
2363 + } else {
2364 + $tbl_custom++;
2365 + }
2366 + } // foreach
2367 +
2368 + echo '<p class="mb0"><b>Currently used WordPress tables</b>, prefixed with <i>' . esc_html($wpdb->prefix) . '</i>, consist of ' . esc_html($tbl_core) . ' standard and ';
2369 + if ($tbl_custom) {
2370 + echo esc_attr($tbl_custom) . ' custom table' . ($tbl_custom == 1 ? '' : 's');
2371 + } else {
2372 + echo 'no custom tables';
2373 + }
2374 + echo ' <span id="wpr-table-details"><a href="#" id="show-table-details">(show details)</a></span>';
2375 + } else {
2376 + echo '<b>Tables information is not available.</b> Something is not working properly on your site. Snapshots won\'t work.';
1241 2377 }
1242 - } // foreach
1243 - }
1244 2378
1245 - return $custom_tables;
1246 - } // get_custom tables
2379 + echo '</div>';
2380 + echo '</div>';
1247 2381
2382 + echo '<div class="card">';
2383 + WP_Reset_Utility::wp_kses_wf($this->get_card_header('User Created Snapshots', 'snapshots-user', array('collapse_button' => 1, 'create_snapshot' => true, 'snapshot_actions' => true)));
2384 + echo '<div class="card-body">';
2385 + if ($snapshots = $this->get_snapshots()) {
2386 + $snapshots = array_reverse($snapshots);
2387 + echo '<table id="wpr-snapshots">';
2388 + echo '<tr><th>Date</th><th>Description</th><th class="ss-size">Size</th><th class="ss-actions">
2389 + <div class="dropdown" style="font-weight:normal; margin-left: 4px;"><a class="button dropdown-toggle" href="#">Actions</a>
2390 + <div class="dropdown-menu">
2391 + <a class="dropdown-item button-pro-feature" href="#">Import a snapshot - <span class="pro-feature" data-feature="snapshots"><span class="pro">PRO</span> Feature</span></a>
2392 + <a class="dropdown-item button-pro-feature" href="#">Cloud snapshots - <span class="pro-feature" data-feature="snapshots"><span class="pro">PRO</span> Feature</span></a>
2393 + </div>
2394 + </div></th></tr>';
2395 + foreach ($snapshots as $ss) {
2396 + echo '<tr id="wpr-ss-' . esc_attr($ss['uid']) . '">';
2397 + if (!empty($ss['name'])) {
2398 + $name = $ss['name'];
2399 + } else {
2400 + $name = 'created on ' . gmdate(get_option('date_format'), strtotime($ss['timestamp'])) . ' @ ' . gmdate(get_option('time_format'), strtotime($ss['timestamp']));
2401 + }
1248 2402
1249 - /**
1250 - * Format file size to human readable string
1251 - *
1252 - * @param int $bytes Size in bytes to format.
1253 - *
1254 - * @return string
1255 - */
1256 - function format_size($bytes) {
1257 - if ($bytes > 1073741824) {
1258 - return number_format_i18n($bytes / 1073741824, 2) . ' GB';
1259 - } elseif ($bytes > 1048576) {
1260 - return number_format_i18n($bytes / 1048576, 1) . ' MB';
1261 - } elseif ($bytes > 1024) {
1262 - return number_format_i18n($bytes / 1024, 1) . ' KB';
1263 - } else {
1264 - return number_format_i18n($bytes, 0) . ' bytes';
1265 - }
1266 - } // format_size
2403 + echo '<td>';
2404 + if (current_time('timestamp') - strtotime($ss['timestamp']) > 12 * HOUR_IN_SECONDS) {
2405 + echo esc_attr(gmdate(get_option('date_format'), strtotime($ss['timestamp']))) . '<br>@ ' . esc_attr(gmdate(get_option('time_format'), strtotime($ss['timestamp'])));
2406 + } else {
2407 + echo esc_attr(human_time_diff(strtotime($ss['timestamp']), current_time('timestamp'))) . ' ago';
2408 + }
2409 + echo '</td>';
1267 2410
2411 + echo '<td>';
2412 + if (!empty($ss['name'])) {
2413 + echo '<b>' . esc_html($ss['name']) . '</b><br>';
2414 + }
2415 + echo esc_attr($ss['tbl_core']) . ' standard &amp; ';
2416 + if ($ss['tbl_custom']) {
2417 + echo esc_attr($ss['tbl_custom']) . ' custom table' . ($ss['tbl_custom'] == 1 ? '' : 's');
2418 + } else {
2419 + echo 'no custom tables';
2420 + }
2421 + echo ' totaling ' . esc_attr(number_format($ss['tbl_rows'])) . ' rows</td>';
2422 + echo '<td class="ss-size">' . esc_attr(WP_Reset_Utility::format_size($ss['tbl_size'])) . '</td>';
2423 + echo '<td>';
2424 + echo '<div class="dropdown">
2425 + <a class="button dropdown-toggle" href="#">Actions</a>
2426 + <div class="dropdown-menu">';
2427 + echo '<a data-title="Current DB tables compared to snapshot %s" data-wait-msg="Comparing. Please wait." data-name="' . esc_attr($name) . '" title="Compare snapshot to current database tables" href="#" class="ss-action compare-snapshot dropdown-item tooltip" data-ss-uid="' . esc_attr($ss['uid']) . '">Compare snapshot to current data</a>';
2428 + echo '<a data-btn-confirm="Restore snapshot" data-text-wait="Restoring snapshot. Please wait." data-text-confirm="Are you sure you want to restore the selected snapshot? There is NO UNDO.<br>Restoring the snapshot will delete all current standard and custom tables and replace them with tables from the snapshot." data-text-done="Snapshot has been restored. Click OK to reload the page with new data." title="Restore snapshot by overwriting current database tables" href="#" class="ss-action restore-snapshot dropdown-item tooltip" data-ss-uid="' . esc_attr($ss['uid']) . '">Restore snapshot</a>';
2429 + echo '<a data-success-msg="Snapshot export created!<br><a href=\'%s\'>Download it</a>" data-wait-msg="Exporting snapshot. Please wait." title="Download snapshot as gzipped SQL dump" href="#" class="ss-action download-snapshot dropdown-item tooltip" data-ss-uid="' . esc_attr($ss['uid']) . '">Download snapshot</a>';
2430 + echo '<a data-btn-confirm="Delete snapshot" data-text-wait="Deleting snapshot. Please wait." data-text-confirm="Are you sure you want to delete the selected snapshot and all its data? There is NO UNDO.<br>Deleting the snapshot will not affect the active database tables in any way." data-text-done="Snapshot has been deleted." title="Permanently delete snapshot" href="#" class="ss-action delete-snapshot dropdown-item tooltip" data-ss-uid="' . esc_attr($ss['uid']) . '">Delete snapshot</a>';
2431 + echo '<a href="#" title="WP Reset PRO feature" data-feature="cloud-wpr" class="ss-action dropdown-item button-pro-feature tooltip">Upload to WP Reset Cloud - <span class="pro-feature" data-feature="cloud-wpr"><span class="pro">PRO</span> Feature</span></a>';
2432 + echo '<a href="#" title="WP Reset PRO feature" class="ss-action dropdown-item button-pro-feature tooltip" data-feature="cloud-general">Upload to Dropbox, Google Drive, or pCloud - <span class="pro-feature" data-feature="cloud-general"><span class="pro">PRO</span> Feature</span></a>';
2433 + echo '</div></div></td>';
2434 + echo '</tr>';
2435 + } // foreach
2436 + echo '</table>';
2437 + echo '<p id="ss-no-snapshots" class="mb0 textcenter hidden">There are no user created snapshots. <a href="#" class="create-new-snapshot">Create a new snapshot.</a></p>';
2438 + } else {
2439 + echo '<p id="ss-no-snapshots" class="mb0 textcenter">There are no user created snapshots. <a href="#" class="create-new-snapshot">Create a new snapshot.</a></p>';
2440 + }
2441 + echo '</div>';
2442 + echo '</div>';
1268 2443
1269 - /**
1270 - * Creates snapshot of current tables by copying them in the DB and saving metadata.
1271 - *
1272 - * @param int $name Optional. Name for the new snapshot.
1273 - *
1274 - * @return array|WP_Error Snapshot details in array on success, or error object on fail.
1275 - */
1276 - function do_create_snapshot($name = '') {
1277 - global $wpdb;
1278 - $snapshots = $this->get_snapshots();
1279 - $snapshot = array();
1280 - $uid = $this->generate_snapshot_uid();
1281 - $tbl_core = $tbl_custom = $tbl_size = $tbl_rows = 0;
2444 + echo '<div class="card">';
2445 + WP_Reset_Utility::wp_kses_wf($this->get_card_header('Automatic Snapshots', 'snapshots-auto', array('collapse_button' => false, 'pro' => true)));
2446 + echo '<div class="card-body">';
2447 + echo '<p><span class="pro-feature pro-feature-text" data-feature="snapshots-auto">WP Reset <span>PRO</span></span> creates automatic snapshots before significant events occur on your site that can cause it to stop working correctly. Plugin, theme and core updates, plugin and theme activations and deactivations all of those can happen in the background without your knowledge. With automatic snapshots, you can roll back any update with a single click. Snapshots can be uploaded to the WP Reset Cloud, Dropbox, Google Drive or pCloud, giving you an extra layer of security.<br>
2448 + Upgrade to <span class="pro-feature pro-feature-text" data-feature="snapshots-auto">WP Reset <span>PRO</span></span> to enable automatic snapshots and give your site an extra layer of safety.</p>';
2449 + echo '</div>';
2450 + echo '</div>';
2451 + } // tab_snapshots
1282 2452
1283 - if (!$uid) {
1284 - return new WP_Error(1, 'Unable to generate a valid snapshot UID.');
1285 - }
1286 2453
1287 - if ($name) {
1288 - $snapshot['name'] = substr(trim($name), 0, 64);
1289 - } else {
1290 - $snapshot['name'] = '';
1291 - }
1292 - $snapshot['uid'] = $uid;
1293 - $snapshot['timestamp'] = current_time('mysql');
2454 + /**
2455 + * Helper function for generating links
2456 + *
2457 + * @param string $placement Optional. UTM content param.
2458 + * @param string $page Optional. Page to link to.
2459 + * @param array $params Optional. Extra URL params.
2460 + * @param string $anchor Optional. URL anchor part.
2461 + *
2462 + * @return string
2463 + */
2464 + function generate_web_link($placement = '', $page = '/', $params = array(), $anchor = '')
2465 + {
2466 + $base_url = 'https://wpreset.com';
1294 2467
1295 - $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1296 - if (is_array($table_status)) {
1297 - foreach ($table_status as $index => $table) {
1298 - if (0 !== stripos($table->Name, $wpdb->prefix)) {
1299 - continue;
2468 + if ('/' != $page) {
2469 + $page = '/' . trim($page, '/') . '/';
1300 2470 }
1301 - if (empty($table->Engine)) {
1302 - continue;
2471 + if ($page == '//') {
2472 + $page = '/';
1303 2473 }
1304 2474
1305 - $tbl_rows += $table->Rows;
1306 - $tbl_size += $table->Data_length + $table->Index_length;
1307 - if (in_array($table->Name, $this->core_tables)) {
1308 - $tbl_core++;
1309 - } else {
1310 - $tbl_custom++;
2475 + if ($placement) {
2476 + $placement = trim($placement);
2477 + $placement = '-' . $placement;
1311 2478 }
1312 2479
1313 - $wpdb->query('OPTIMIZE TABLE ' . $table->Name);
1314 - $wpdb->query('CREATE TABLE ' . $uid . '_' . $table->Name .' LIKE ' . $table->Name);
1315 - $wpdb->query('INSERT ' . $uid . '_' . $table->Name . ' SELECT * FROM ' . $table->Name);
1316 - } // foreach
1317 - } else {
1318 - return new WP_Error(1, 'Can\'t get table status data.');
1319 - }
2480 + $parts = array_merge(array('ref' => 'wp-reset-free' . $placement), $params);
1320 2481
1321 - $snapshot['tbl_core'] = $tbl_core;
1322 - $snapshot['tbl_custom'] = $tbl_custom;
1323 - $snapshot['tbl_rows'] = $tbl_rows;
1324 - $snapshot['tbl_size'] = $tbl_size;
2482 + if (!empty($anchor)) {
2483 + $anchor = '#' . trim($anchor, '#');
2484 + }
1325 2485
2486 + $out = $base_url . $page . '?' . http_build_query($parts, '', '&amp;') . $anchor;
1326 2487
1327 - $snapshots[$uid] = $snapshot;
1328 - update_option('wp-reset-snapshots', $snapshots);
2488 + return $out;
2489 + } // generate_web_link
1329 2490
1330 - return $snapshot;
1331 - } // create_snapshot
1332 2491
2492 + /**
2493 + * Returns all saved snapshots from DB
2494 + *
2495 + * @return array
2496 + */
2497 + function get_snapshots()
2498 + {
2499 + $snapshots = get_option('wp-reset-snapshots', array());
1333 2500
1334 - /**
1335 - * Delete snapshot metadata and tables from DB
1336 - *
1337 - * @param string $uid Snapshot unique 6-char ID.
1338 - *
1339 - * @return bool|WP_Error True on success, or error object on fail.
1340 - */
1341 - function do_delete_snapshot($uid = '') {
1342 - global $wpdb;
1343 - $snapshots = $this->get_snapshots();
2501 + return $snapshots;
2502 + } // get_snapshots
1344 2503
1345 - if (strlen($uid) != 6) {
1346 - return new WP_Error(1, 'Invalid UID format.');
1347 - }
1348 2504
1349 - if (!isset($snapshots[$uid])) {
1350 - return new WP_Error(1, 'Unknown snapshot ID.');
1351 - }
2505 + /**
2506 + * Returns all custom table names, with prefix
2507 + *
2508 + * @return array
2509 + */
2510 + function get_custom_tables()
2511 + {
2512 + global $wpdb;
2513 + $custom_tables = array();
1352 2514
1353 - $tables = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array($uid . '\_%')));
1354 - foreach ($tables as $table) {
1355 - $wpdb->query('DROP TABLE IF EXISTS ' . $table);
1356 - }
2515 + // phpcs:ignore db call warning as we are using uncommon queries
2516 + $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2517 + if (is_array($table_status)) {
2518 + foreach ($table_status as $index => $table) {
2519 + if (0 !== stripos($table->Name, $wpdb->prefix)) {
2520 + continue;
2521 + }
2522 + if (empty($table->Engine)) {
2523 + continue;
2524 + }
1357 2525
1358 - unset($snapshots[$uid]);
1359 - update_option('wp-reset-snapshots', $snapshots);
2526 + if (false === in_array($table->Name, $this->core_tables)) {
2527 + $custom_tables[] = array('name' => $table->Name, 'rows' => $table->Rows, 'data_length' => $table->Data_length, 'index_length' => $table->Index_length);
2528 + }
2529 + } // foreach
2530 + }
1360 2531
1361 - return true;
1362 - } // delete_snapshot
2532 + return $custom_tables;
2533 + } // get_custom tables
1363 2534
1364 2535
1365 - /**
1366 - * Exports snapshot as SQL dump; saved in gzipped file in WP_CONTENT folder.
1367 - *
1368 - * @param string $uid Snapshot unique 6-char ID.
1369 - *
1370 - * @return string|WP_Error Export base filename, or error object on fail.
1371 - */
1372 - function do_export_snapshot($uid = '') {
1373 - global $wpdb;
1374 - $snapshots = $this->get_snapshots();
2536 + /**
2537 + * Creates snapshot of current tables by copying them in the DB and saving metadata.
2538 + *
2539 + * @param int $name Optional. Name for the new snapshot.
2540 + *
2541 + * @return array|WP_Error Snapshot details in array on success, or error object on fail.
2542 + */
2543 + function do_create_snapshot($name = '')
2544 + {
2545 + global $wpdb;
2546 + $snapshots = $this->get_snapshots();
2547 + $snapshot = array();
2548 + $uid = $this->generate_snapshot_uid();
2549 + $tbl_core = $tbl_custom = $tbl_size = $tbl_rows = 0;
1375 2550
1376 - if (strlen($uid) != 6) {
1377 - return new WP_Error(1, 'Invalid snapshot ID format.');
1378 - }
2551 + if (!$uid) {
2552 + return new WP_Error(1, 'Unable to generate a valid snapshot UID.');
2553 + }
1379 2554
1380 - if (!isset($snapshots[$uid])) {
1381 - return new WP_Error(1, 'Unknown snapshot ID.');
1382 - }
2555 + if ($name) {
2556 + $snapshot['name'] = substr(trim($name), 0, 64);
2557 + } else {
2558 + $snapshot['name'] = '';
2559 + }
2560 + $snapshot['uid'] = $uid;
2561 + $snapshot['timestamp'] = current_time('mysql');
1383 2562
1384 - require_once $this->plugin_dir . 'libs/dumper.php';
2563 + // phpcs:ignore db call warning as we are using uncommon queries
2564 + $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2565 + if (is_array($table_status)) {
2566 + foreach ($table_status as $index => $table) {
2567 + if (0 !== stripos($table->Name, $wpdb->prefix)) {
2568 + continue;
2569 + }
2570 + if (empty($table->Engine)) {
2571 + continue;
2572 + }
1385 2573
1386 - try {
1387 - $world_dumper = Shuttle_Dumper::create(array(
1388 - 'host' => DB_HOST,
1389 - 'username' => DB_USER,
1390 - 'password' => DB_PASSWORD,
1391 - 'db_name' => DB_NAME,
1392 - ));
2574 + $tbl_rows += $table->Rows;
2575 + $tbl_size += $table->Data_length + $table->Index_length;
2576 + if (in_array($table->Name, $this->core_tables)) {
2577 + $tbl_core++;
2578 + } else {
2579 + $tbl_custom++;
2580 + }
1393 2581
1394 - $folder = wp_mkdir_p(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder);
1395 - if (!$folder) {
1396 - return new WP_Error(1, 'Unable to create wp-content/' . $this->snapshots_folder . '/ folder.');
1397 - }
2582 + $wpdb->wpreset_snapshot_table_name = $table->Name;
2583 + $wpdb->wpreset_snapshot_table_copy_name = $uid . '_' . $table->Name;
2584 +
2585 + // phpcs:ignore db call warning as we are using uncommon queries
2586 + $wpdb->query("OPTIMIZE TABLE " . $wpdb->wpreset_snapshot_table_name); // phpcs:ignore
2587 + $wpdb->query("CREATE TABLE " . $wpdb->wpreset_snapshot_table_copy_name . " LIKE " . $wpdb->wpreset_snapshot_table_name); // phpcs:ignore
2588 + $wpdb->query("INSERT " . $wpdb->wpreset_snapshot_table_copy_name . " SELECT * FROM " . $wpdb->wpreset_snapshot_table_name); // phpcs:ignore
2589 + } // foreach
2590 + } else {
2591 + return new WP_Error(1, 'Can\'t get table status data.');
2592 + }
1398 2593
1399 - $world_dumper->dump(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/wp-reset-snapshot-' . $uid . '.sql.gz', $uid . '_');
1400 - } catch(Shuttle_Exception $e) {
1401 - return new WP_Error(1, "Couldn't dump snapshot: " . $e->getMessage());
1402 - }
2594 + $snapshot['tbl_core'] = $tbl_core;
2595 + $snapshot['tbl_custom'] = $tbl_custom;
2596 + $snapshot['tbl_rows'] = $tbl_rows;
2597 + $snapshot['tbl_size'] = $tbl_size;
1403 2598
1404 - return 'wp-reset-snapshot-' . $uid . '.sql.gz';
1405 - } // export_snapshot
1406 2599
2600 + $snapshots[$uid] = $snapshot;
2601 + update_option('wp-reset-snapshots', $snapshots);
1407 2602
1408 - /**
1409 - * Replace current tables with ones in snapshot.
1410 - *
1411 - * @param string $uid Snapshot unique 6-char ID.
1412 - *
1413 - * @return bool|WP_Error True on success, or error object on fail.
1414 - */
1415 - function do_restore_snapshot($uid = '') {
1416 - global $wpdb;
1417 - $new_tables = array();
1418 - $snapshots = $this->get_snapshots();
2603 + do_action('wp_reset_create_snapshot', $uid, $snapshot);
1419 2604
1420 - if (($res = $this->verify_snapshot_integrity($uid)) !== true) {
1421 - return $res;
1422 - }
2605 + return $snapshot;
2606 + } // create_snapshot
1423 2607
1424 - $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1425 - if (is_array($table_status)) {
1426 - foreach ($table_status as $index => $table) {
1427 - if (0 !== stripos($table->Name, $uid . '_')) {
1428 - continue;
2608 +
2609 + /**
2610 + * Delete snapshot metadata and tables from DB
2611 + *
2612 + * @param string $uid Snapshot unique 6-char ID.
2613 + *
2614 + * @return bool|WP_Error True on success, or error object on fail.
2615 + */
2616 + function do_delete_snapshot($uid = '')
2617 + {
2618 + global $wpdb;
2619 + $snapshots = $this->get_snapshots();
2620 +
2621 + if (strlen($uid) != 6) {
2622 + return new WP_Error(1, 'Invalid UID format.');
1429 2623 }
1430 - if (empty($table->Engine)) {
1431 - continue;
2624 +
2625 + if (!isset($snapshots[$uid])) {
2626 + return new WP_Error(1, 'Unknown snapshot ID.');
1432 2627 }
1433 2628
1434 - $new_tables[] = $table->Name;
1435 - } // foreach
1436 - } else {
1437 - return new WP_Error(1, 'Can\'t get table status data.');
1438 - }
2629 + // phpcs:ignore db call warning as we are using uncommon queries
2630 + $tables = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array($uid . '\_%'))); // phpcs:ignore
2631 + foreach ($tables as $table) {
2632 + $wpdb->wpreset_snapshot_table = $table;
2633 + $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2634 + }
1439 2635
1440 - foreach ($table_status as $index => $table) {
1441 - if (0 !== stripos($table->Name, $wpdb->prefix)) {
1442 - continue;
1443 - }
1444 - if (empty($table->Engine)) {
1445 - continue;
1446 - }
2636 + $snapshot_copy = $snapshots[$uid];
2637 + unset($snapshots[$uid]);
2638 + update_option('wp-reset-snapshots', $snapshots);
1447 2639
1448 - $wpdb->query('DROP TABLE ' . $table->Name);
1449 - } // foreach
2640 + do_action('wp_reset_delete_snapshot', $uid, $snapshot_copy);
1450 2641
1451 - // copy snapshot tables to original name
1452 - foreach ($new_tables as $table) {
1453 - $new_name = str_replace($uid . '_', '', $table);
2642 + return true;
2643 + } // delete_snapshot
1454 2644
1455 - $wpdb->query('CREATE TABLE ' . $new_name . ' LIKE ' . $table);
1456 - $wpdb->query('INSERT ' . $new_name . ' SELECT * FROM ' . $table);
1457 - }
1458 2645
1459 - wp_cache_flush();
1460 - update_option('wp-reset', $this->options);
1461 - update_option('wp-reset-snapshots', $snapshots);
2646 + /**
2647 + * Exports snapshot as SQL dump; saved in gzipped file in WP_CONTENT folder.
2648 + *
2649 + * @param string $uid Snapshot unique 6-char ID.
2650 + *
2651 + * @return string|WP_Error Export base filename, or error object on fail.
2652 + */
2653 + function do_export_snapshot($uid = '')
2654 + {
2655 + global $wp_filesystem;
2656 + $this->wp_init_filesystem();
2657 +
2658 + $snapshots = $this->get_snapshots();
1462 2659
1463 - return true;
1464 - } // restore_snapshot
2660 + if (strlen($uid) != 6) {
2661 + return new WP_Error(1, 'Invalid snapshot ID format.');
2662 + }
1465 2663
2664 + if (!isset($snapshots[$uid])) {
2665 + return new WP_Error(1, 'Unknown snapshot ID.');
2666 + }
1466 2667
1467 - /**
1468 - * Verifies snapshot integrity by comparing metadata and data in DB
1469 - *
1470 - * @param string $uid Snapshot unique 6-char ID.
1471 - *
1472 - * @return bool|WP_Error True on success, or error object on fail.
1473 - */
1474 - function verify_snapshot_integrity($uid) {
1475 - global $wpdb;
1476 - $tbl_core = $tbl_custom = 0;
1477 - $snapshots = $this->get_snapshots();
2668 + require_once $this->plugin_dir . 'libs/dumper.php';
1478 2669
1479 - if (strlen($uid) != 6) {
1480 - return new WP_Error(1, 'Invalid snapshot ID format.');
1481 - }
2670 + $snapshot_file_uid = md5($this->generate_snapshot_uid(10));
2671 + try {
2672 + $world_dumper = WPR_Shuttle_Dumper::create(array(
2673 + 'host' => DB_HOST,
2674 + 'username' => DB_USER,
2675 + 'password' => DB_PASSWORD,
2676 + 'db_name' => DB_NAME,
2677 + ));
1482 2678
1483 - if (!isset($snapshots[$uid])) {
1484 - return new WP_Error(1, 'Unknown snapshot ID.');
1485 - }
2679 + $folder = wp_mkdir_p(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder);
2680 + if (!$folder) {
2681 + return new WP_Error(1, 'Unable to create wp-content/' . $this->snapshots_folder . '/ folder.');
2682 + }
1486 2683
1487 - $snapshot = $snapshots[$uid];
2684 + $htaccess_content = 'AddType application/octet-stream .gz' . PHP_EOL;
2685 + $htaccess_content .= 'Options -Indexes' . PHP_EOL;
1488 2686
1489 - $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1490 - if (is_array($table_status)) {
1491 - foreach ($table_status as $index => $table) {
1492 - if (0 !== stripos($table->Name, $uid . '_')) {
1493 - continue;
2687 + $wp_filesystem->put_contents(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/.htaccess', $htaccess_content);
2688 +
2689 + $world_dumper->dump(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/wp-reset-snapshot-' . $snapshot_file_uid . '.sql.gz', $uid . '_');
2690 + } catch (Shuttle_Exception $e) {
2691 + return new WP_Error(1, 'Couldn\'t create snapshot: ' . $e->getMessage());
1494 2692 }
1495 - if (empty($table->Engine)) {
1496 - continue;
2693 +
2694 + do_action('wp_reset_export_snapshot', 'wp-reset-snapshot-' . $snapshot_file_uid . '.sql.gz');
2695 +
2696 + return 'wp-reset-snapshot-' . $snapshot_file_uid . '.sql.gz';
2697 + } // export_snapshot
2698 +
2699 +
2700 + /**
2701 + * Replace current tables with ones in snapshot.
2702 + *
2703 + * @param string $uid Snapshot unique 6-char ID.
2704 + *
2705 + * @return bool|WP_Error True on success, or error object on fail.
2706 + */
2707 + function do_restore_snapshot($uid = '')
2708 + {
2709 + // phpcs:ignore db call warning as we are using uncommon queries
2710 + global $wpdb;
2711 + $new_tables = array();
2712 + $snapshots = $this->get_snapshots();
2713 +
2714 + if (($res = $this->verify_snapshot_integrity($uid)) !== true) {
2715 + return $res;
1497 2716 }
1498 2717
1499 - if (in_array(str_replace($uid . '_', '', $table->Name), $this->core_tables)) {
1500 - $tbl_core++;
2718 + $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2719 + if (is_array($table_status)) {
2720 + foreach ($table_status as $index => $table) {
2721 + if (0 !== stripos($table->Name, $uid . '_')) {
2722 + continue;
2723 + }
2724 + if (empty($table->Engine)) {
2725 + continue;
2726 + }
2727 +
2728 + $new_tables[] = $table->Name;
2729 + } // foreach
1501 2730 } else {
1502 - $tbl_custom++;
2731 + return new WP_Error(1, 'Can\'t get table status data.');
1503 2732 }
1504 - } // foreach
1505 2733
1506 - if ($tbl_core != $snapshot['tbl_core'] || $tbl_custom != $snapshot['tbl_custom']) {
1507 - 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.');
1508 - }
1509 - } else {
1510 - return new WP_Error(1, 'Can\'t get table status data.');
1511 - }
2734 + foreach ($table_status as $index => $table) {
2735 + if (0 !== stripos($table->Name, $wpdb->prefix)) {
2736 + continue;
2737 + }
2738 + if (empty($table->Engine)) {
2739 + continue;
2740 + }
1512 2741
1513 - return true;
1514 - } // verify_snapshot_integrity
2742 + $wpdb->wpreset_snapshot_table = $table->Name;
2743 + $wpdb->query('DROP TABLE ' . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2744 + } // foreach
1515 2745
2746 + // copy snapshot tables to original name
2747 + foreach ($new_tables as $table) {
2748 + $wpdb->wpreset_snapshot_table = $table;
2749 + $wpdb->wpreset_snapshot_table_new = str_replace($uid . '_', '', $table);
1516 2750
1517 - /**
1518 - * Compares a selected snapshot with the current table set in DB
1519 - *
1520 - * @param string $uid Snapshot unique 6-char ID.
1521 - *
1522 - * @return string|WP_Error Formatted table with details on success, or error object on fail.
1523 - */
1524 - function do_compare_snapshots($uid) {
1525 - global $wpdb;
1526 - $tbl_core = $tbl_custom = 0;
1527 - $current = $snapshot = array();
1528 - $out = $out2 = $out3 = '';
2751 + $wpdb->query("CREATE TABLE " . $wpdb->wpreset_snapshot_table_new . " LIKE " . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2752 + $wpdb->query("INSERT " . $wpdb->wpreset_snapshot_table_new . " SELECT * FROM " . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2753 + }
1529 2754
1530 - if (($res = $this->verify_snapshot_integrity($uid)) !== true) {
1531 - return $res;
1532 - }
2755 + wp_cache_flush();
2756 + update_option('wp-reset', $this->options);
2757 + update_option('wp-reset-snapshots', $snapshots);
1533 2758
1534 - $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1535 - foreach ($table_status as $index => $table) {
1536 - if (empty($table->Engine)) {
1537 - continue;
1538 - }
2759 + do_action('wp_reset_restore_snapshot', $uid);
1539 2760
1540 - if (0 !== stripos($table->Name, $uid . '_') && 0 !== stripos($table->Name, $wpdb->prefix)) {
1541 - continue;
1542 - }
2761 + return true;
2762 + } // restore_snapshot
1543 2763
1544 - $info = array();
1545 - $info['rows'] = $table->Rows;
1546 - $info['size_data'] = $table->Data_length;
1547 - $info['size_index'] = $table->Index_length;
1548 - $schema = $wpdb->get_row('SHOW CREATE TABLE ' . $table->Name, ARRAY_N);
1549 - $info['schema'] = $schema[1];
1550 - $info['engine'] = $table->Engine;
1551 - $info['fullname'] = $table->Name;
1552 - $basename = str_replace(array($uid . '_'), array(''), $table->Name);
1553 - $info['basename'] = $basename;
1554 - $info['corename'] = str_replace(array($wpdb->prefix), array(''), $basename);
1555 - $info['uid'] = $uid;
1556 2764
1557 - if (0 === stripos($table->Name, $uid . '_')) {
1558 - $snapshot[$basename] = $info;
1559 - }
2765 + /**
2766 + * Verifies snapshot integrity by comparing metadata and data in DB
2767 + *
2768 + * @param string $uid Snapshot unique 6-char ID.
2769 + *
2770 + * @return bool|WP_Error True on success, or error object on fail.
2771 + */
2772 + function verify_snapshot_integrity($uid)
2773 + {
2774 + // phpcs:ignore db call warning as we are using uncommon queries
2775 + global $wpdb;
2776 + $tbl_core = $tbl_custom = 0;
2777 + $snapshots = $this->get_snapshots();
1560 2778
1561 - if (0 === stripos($table->Name, $wpdb->prefix)) {
1562 - $info['uid'] = '';
1563 - $current[$basename] = $info;
1564 - }
1565 - } // foreach
2779 + if (strlen($uid) != 6) {
2780 + return new WP_Error(1, 'Invalid snapshot ID format.');
2781 + }
1566 2782
1567 - $in_both = array_keys(array_intersect_key($current, $snapshot));
1568 - $in_current_only = array_diff_key($current, $snapshot);
1569 - $in_snapshot_only = array_diff_key($snapshot, $current);
2783 + if (!isset($snapshots[$uid])) {
2784 + return new WP_Error(1, 'Unknown snapshot ID.');
2785 + }
1570 2786
1571 - $out .= '<br><br>';
1572 - foreach ($in_current_only as $table) {
1573 - $out .= '<div class="wpr-table-container in-current-only" data-table="' . $table['basename'] . '">';
1574 - $out .= '<table>';
1575 - $out .= '<tr title="Click to show/hide more info" class="wpr-table-missing header-row">';
1576 - $out .= '<td><b>' . $table['fullname'] . '</b></td>';
1577 - $out .= '<td>table is not present in snapshot<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
1578 - $out .= '</tr>';
1579 - $out .= '<tr class="hidden">';
1580 - $out .= '<td>';
1581 - $out .= '<p>' . 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.</p>';
1582 - $out .= '<pre>' . $table['schema'] . '</pre>';
1583 - $out .= '</td>';
1584 - $out .= '<td>&nbsp;</td>';
1585 - $out .= '</tr>';
1586 - $out .= '</table>';
1587 - $out .= '</div>';
1588 - } // foreach in current only
2787 + $snapshot = $snapshots[$uid];
1589 2788
1590 - foreach ($in_snapshot_only as $table) {
1591 - $out .= '<div class="wpr-table-container in-snapshot-only" data-table="' . $table['basename'] . '">';
1592 - $out .= '<table>';
1593 - $out .= '<tr title="Click to show/hide more info" class="wpr-table-missing header-row">';
1594 - $out .= '<td>table is not present in current tables</td>';
1595 - $out .= '<td><b>' . $table['fullname'] . '</b><span class="dashicons dashicons-arrow-down-alt2"></span></td>';
1596 - $out .= '</tr>';
1597 - $out .= '<tr class="hidden">';
1598 - $out .= '<td>&nbsp;</td>';
1599 - $out .= '<td>';
1600 - $out .= '<p>' . 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.</p>';
1601 - $out .= '<pre>' . $table['schema'] . '</pre>';
1602 - $out .= '</td>';
1603 - $out .= '</tr>';
1604 - $out .= '</table>';
1605 - $out .= '</div>';
1606 - } // foreach in snapshot only
2789 + $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2790 + if (is_array($table_status)) {
2791 + foreach ($table_status as $index => $table) {
2792 + if (0 !== stripos($table->Name, $uid . '_')) {
2793 + continue;
2794 + }
2795 + if (empty($table->Engine)) {
2796 + continue;
2797 + }
1607 2798
1608 - foreach ($in_both as $tablename) {
1609 - $tbl_current = $current[$tablename];
1610 - $tbl_snapshot = $snapshot[$tablename];
2799 + if (in_array(str_replace($uid . '_', '', $table->Name), $this->core_tables)) {
2800 + $tbl_core++;
2801 + } else {
2802 + $tbl_custom++;
2803 + }
2804 + } // foreach
1611 2805
1612 - $schema1 = preg_replace('/(auto_increment=)([0-9]*) /i', '${1}1 ', $tbl_current['schema'], 1);
1613 - $schema2 = preg_replace('/(auto_increment=)([0-9]*) /i', '${1}1 ', $tbl_snapshot['schema'], 1);
1614 - $tbl_snapshot['tmp_schema'] = str_replace($tbl_snapshot['uid'] . '_' . $tablename, $tablename, $tbl_snapshot['schema']);
1615 - $schema2 = str_replace($tbl_snapshot['uid'] . '_' . $tablename, $tablename, $schema2);
2806 + if ($tbl_core != $snapshot['tbl_core'] || $tbl_custom != $snapshot['tbl_custom']) {
2807 + 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.');
2808 + }
2809 + } else {
2810 + return new WP_Error(1, 'Can\'t get table status data.');
2811 + }
1616 2812
1617 - if ($tbl_current['rows'] == $tbl_snapshot['rows'] && $tbl_current['schema'] == $tbl_snapshot['tmp_schema']) {
1618 - $out3 .= '<div class="wpr-table-container identical" data-table="' . $tablename . '">';
1619 - $out3 .= '<table>';
1620 - $out3 .= '<tr title="Click to show/hide more info" class="wpr-table-match header-row">';
1621 - $out3 .= '<td><b>' . $tbl_current['fullname'] . '</b></td>';
1622 - $out3 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b><span class="dashicons dashicons-arrow-down-alt2"></span></td>';
1623 - $out3 .= '</tr>';
1624 - $out3 .= '<tr class="hidden">';
1625 - $out3 .= '<td>';
1626 - $out3 .= '<p>' . 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.</p>';
1627 - $out3 .= '<pre>' . $tbl_current['schema'] . '</pre>';
1628 - $out3 .= '</td>';
1629 - $out3 .= '<td>';
1630 - $out3 .= '<p>' . 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.</p>';
1631 - $out3 .= '<pre>' . $tbl_snapshot['schema'] . '</pre>';
1632 - $out3 .= '</td>';
1633 - $out3 .= '</tr>';
1634 - $out3 .= '</table>';
1635 - $out3 .= '</div>';
1636 - } elseif ($schema1 != $schema2) {
1637 - require_once $this->plugin_dir . 'libs/diff.php';
1638 - require_once $this->plugin_dir . 'libs/diff/Renderer/Html/SideBySide.php';
1639 - $diff = new Diff(explode("\n", $tbl_current['schema']), explode("\n", $tbl_snapshot['schema']), array('ignoreWhitespace' => false));
1640 - $renderer = new Diff_Renderer_Html_SideBySide;
2813 + return true;
2814 + } // verify_snapshot_integrity
1641 2815
1642 - $out2 .= '<div class="wpr-table-container" data-table="' . $tbl_current['basename'] . '">';
1643 - $out2 .= '<table>';
1644 - $out2 .= '<tr title="Click to show/hide more info" class="wpr-table-difference header-row">';
1645 - $out2 .= '<td><b>' . $tbl_current['fullname'] . '</b> table schemas do not match</td>';
1646 - $out2 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b> table schemas do not match<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
1647 - $out2 .= '</tr>';
1648 - $out2 .= '<tr class="hidden">';
1649 - $out2 .= '<td>';
1650 - $out2 .= '<p>' . 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.</p>';
1651 - $out2 .= '</td>';
1652 - $out2 .= '<td>';
1653 - $out2 .= '<p>' . 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.</p>';
1654 - $out2 .= '</td>';
1655 - $out2 .= '</tr>';
1656 - $out2 .= '<tr class="hidden">';
1657 - $out2 .= '<td colspan="2" class="no-padding">';
1658 - $out2 .= $diff->Render($renderer);
1659 - $out2 .= '</td>';
1660 - $out2 .= '</tr>';
1661 - $out2 .= '</table>';
1662 - $out2 .= '</div>';
1663 - } else {
1664 - $out2 .= '<div class="wpr-table-container" data-table="' . $tbl_current['basename'] . '">';
1665 - $out2 .= '<table>';
1666 - $out2 .= '<tr title="Click to show/hide more info" class="wpr-table-difference header-row">';
1667 - $out2 .= '<td><b>' . $tbl_current['fullname'] . '</b> data in tables does not match</td>';
1668 - $out2 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b> data in tables does not match<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
1669 - $out2 .= '</tr>';
1670 - $out2 .= '<tr class="hidden">';
1671 - $out2 .= '<td>';
1672 - $out2 .= '<p>' . 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.</p>';
1673 - $out2 .= '</td>';
1674 - $out2 .= '<td>';
1675 - $out2 .= '<p>' . 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.</p>';
1676 - $out2 .= '</td>';
1677 - $out2 .= '</tr>';
1678 2816
1679 - $out2 .= '<tr class="hidden">';
1680 - $out2 .= '<td colspan="2">';
1681 - if ($tbl_current['corename'] == 'options') {
1682 - $ss_prefix = $tbl_snapshot['uid'] . '_' . $wpdb->prefix;
1683 - $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;");
1684 - $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;");
1685 - $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;");
1686 - $out2 .= '<table class="table_diff">';
1687 - $out2 .= '<tr><td style="width: 100px;"><b>Option Name</b></td><td><b>Current Value</b></td><td><b>Snapshot Value</b></td></tr>';
1688 - foreach ($diff_rows as $row) {
1689 - $out2 .= '<tr>';
1690 - $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1691 - $out2 .= '<td>' . (empty($row->current_value)? '<i>empty</i>': $row->current_value) . '</td>';
1692 - $out2 .= '<td>' . (empty($row->snapshot_value)? '<i>empty</i>': $row->snapshot_value) . '</td>';
1693 - $out2 .= '</tr>';
1694 - } // foreach
1695 - foreach ($only_current as $row) {
1696 - $out2 .= '<tr>';
1697 - $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1698 - $out2 .= '<td>' . (empty($row->current_value)? '<i>empty</i>': $row->current_value) . '</td>';
1699 - $out2 .= '<td><i>not found in snapshot</i></td>';
1700 - $out2 .= '</tr>';
1701 - } // foreach
1702 - foreach ($only_current as $row) {
1703 - $out2 .= '<tr>';
1704 - $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1705 - $out2 .= '<td><i>not found in current tables</i></td>';
1706 - $out2 .= '<td>' . (empty($row->snapshot_value)? '<i>empty</i>': $row->snapshot_value) . '</td>';
1707 - $out2 .= '</tr>';
1708 - } // foreach
1709 - $out2 .= '</table>';
1710 - } else {
1711 - $out2 .= '<p class="textcenter">Detailed data diff is not available for this table.</p>';
2817 + /**
2818 + * Compares a selected snapshot with the current table set in DB
2819 + *
2820 + * @param string $uid Snapshot unique 6-char ID.
2821 + *
2822 + * @return string|WP_Error Formatted table with details on success, or error object on fail.
2823 + */
2824 + function do_compare_snapshots($uid)
2825 + {
2826 + // phpcs:ignore db call warning as we are using uncommon queries
2827 + global $wpdb;
2828 + $current = $snapshot = array();
2829 + $out = $out2 = $out3 = '';
2830 +
2831 + if (($res = $this->verify_snapshot_integrity($uid)) !== true) {
2832 + return $res;
1712 2833 }
1713 - $out2 .= '</td>';
1714 - $out2 .= '</tr>';
1715 2834
1716 - $out2 .= '</table>';
1717 - $out2 .= '</div>';
1718 - }
1719 - } // foreach in both
2835 + $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2836 + foreach ($table_status as $index => $table) {
2837 + if (empty($table->Engine)) {
2838 + continue;
2839 + }
1720 2840
1721 - return $out . $out2 . $out3;
1722 - } // do_compare_snapshots
2841 + if (0 !== stripos($table->Name, $uid . '_') && 0 !== stripos($table->Name, $wpdb->prefix)) {
2842 + continue;
2843 + }
1723 2844
2845 + $info = array();
2846 + $info['rows'] = $table->Rows;
2847 + $info['size_data'] = $table->Data_length;
2848 + $info['size_index'] = $table->Index_length;
2849 + $wpdb->wpreset_table_name = $table->Name;
2850 + $schema = $wpdb->get_row('SHOW CREATE TABLE ' . $wpdb->wpreset_table_name, ARRAY_N); // phpcs:ignore
2851 + $info['schema'] = $schema[1];
2852 + $info['engine'] = $table->Engine;
2853 + $info['fullname'] = $table->Name;
2854 + $basename = str_replace(array($uid . '_'), array(''), $table->Name);
2855 + $info['basename'] = $basename;
2856 + $info['corename'] = str_replace(array($wpdb->prefix), array(''), $basename);
2857 + $info['uid'] = $uid;
1724 2858
1725 - /**
1726 - * Generates a unique 6-char snapshot ID; verified non-existing
1727 - *
1728 - * @return string
1729 - */
1730 - function generate_snapshot_uid() {
1731 - global $wpdb;
1732 - $snapshots = $this->get_snapshots();
1733 - $cnt = 0;
1734 - $uid = false;
2859 + if (0 === stripos($table->Name, $uid . '_')) {
2860 + $snapshot[$basename] = $info;
2861 + }
1735 2862
1736 - do {
1737 - $cnt++;
1738 - $uid = sprintf('%06x', mt_rand(0, 0xFFFFFF));
2863 + if (0 === stripos($table->Name, $wpdb->prefix)) {
2864 + $info['uid'] = '';
2865 + $current[$basename] = $info;
2866 + }
2867 + } // foreach
1739 2868
1740 - $verify_db = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array('%' . $uid . '%')));
1741 - } while (!empty($verify_db) && isset($snapshots[$uid]) && $cnt < 30);
2869 + $in_both = array_keys(array_intersect_key($current, $snapshot));
2870 + $in_current_only = array_diff_key($current, $snapshot);
2871 + $in_snapshot_only = array_diff_key($snapshot, $current);
1742 2872
1743 - if ($cnt == 30) {
1744 - $uid = false;
1745 - }
2873 + $out .= '<br><br>';
2874 + foreach ($in_current_only as $table) {
2875 + $out .= '<div class="wpr-table-container in-current-only" data-table="' . esc_attr($table['basename']) . '">';
2876 + $out .= '<table>';
2877 + $out .= '<tr title="Click to show/hide more info" class="wpr-table-missing header-row">';
2878 + $out .= '<td><b>' . $table['fullname'] . '</b></td>';
2879 + $out .= '<td>table is not present in snapshot<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2880 + $out .= '</tr>';
2881 + $out .= '<tr class="hidden">';
2882 + $out .= '<td>';
2883 + $out .= '<p>' . number_format($table['rows']) . ' row' . ($table['rows'] == 1 ? '' : 's') . ' totaling ' . WP_Reset_Utility::format_size($table['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($table['size_index']) . ' in index.</p>';
2884 + $out .= '<pre>' . $table['schema'] . '</pre>';
2885 + $out .= '</td>';
2886 + $out .= '<td>&nbsp;</td>';
2887 + $out .= '</tr>';
2888 + $out .= '</table>';
2889 + $out .= '</div>';
2890 + } // foreach in current only
1746 2891
1747 - return $uid;
1748 - } // generate_snapshot_uid
2892 + foreach ($in_snapshot_only as $table) {
2893 + $out .= '<div class="wpr-table-container in-snapshot-only" data-table="' . esc_attr($table['basename']) . '">';
2894 + $out .= '<table>';
2895 + $out .= '<tr title="Click to show/hide more info" class="wpr-table-missing header-row">';
2896 + $out .= '<td>table is not present in current tables</td>';
2897 + $out .= '<td><b>' . esc_html($table['fullname']) . '</b><span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2898 + $out .= '</tr>';
2899 + $out .= '<tr class="hidden">';
2900 + $out .= '<td>&nbsp;</td>';
2901 + $out .= '<td>';
2902 + $out .= '<p>' . number_format($table['rows']) . ' row' . ($table['rows'] == 1 ? '' : 's') . ' totaling ' . WP_Reset_Utility::format_size($table['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($table['size_index']) . ' in index.</p>';
2903 + $out .= '<pre>' . $table['schema'] . '</pre>';
2904 + $out .= '</td>';
2905 + $out .= '</tr>';
2906 + $out .= '</table>';
2907 + $out .= '</div>';
2908 + } // foreach in snapshot only
1749 2909
2910 + foreach ($in_both as $tablename) {
2911 + $tbl_current = $current[$tablename];
2912 + $tbl_snapshot = $snapshot[$tablename];
1750 2913
1751 - /**
1752 - * Helper function for adding plugins to featured list
1753 - *
1754 - * @return array
1755 - */
1756 - function featured_plugins_tab($args) {
1757 - add_filter('plugins_api_result', array($this, 'plugins_api_result'), 10, 3);
2914 + $schema1 = preg_replace('/(auto_increment=)([0-9]*) /i', '${1}1 ', $tbl_current['schema'], 1);
2915 + $schema2 = preg_replace('/(auto_increment=)([0-9]*) /i', '${1}1 ', $tbl_snapshot['schema'], 1);
2916 + $tbl_snapshot['tmp_schema'] = str_replace($tbl_snapshot['uid'] . '_' . $tablename, $tablename, $tbl_snapshot['schema']);
2917 + $schema2 = str_replace($tbl_snapshot['uid'] . '_' . $tablename, $tablename, $schema2);
1758 2918
1759 - return $args;
1760 - } // featured_plugins_tab
2919 + if ($tbl_current['rows'] == $tbl_snapshot['rows'] && $tbl_current['schema'] == $tbl_snapshot['tmp_schema']) {
2920 + $out3 .= '<div class="wpr-table-container identical" data-table="' . esc_attr($tablename) . '">';
2921 + $out3 .= '<table>';
2922 + $out3 .= '<tr title="Click to show/hide more info" class="wpr-table-match header-row">';
2923 + $out3 .= '<td><b>' . $tbl_current['fullname'] . '</b></td>';
2924 + $out3 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b><span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2925 + $out3 .= '</tr>';
2926 + $out3 .= '<tr class="hidden">';
2927 + $out3 .= '<td>';
2928 + $out3 .= '<p>' . number_format($tbl_current['rows']) . ' rows totaling ' . WP_Reset_Utility::format_size($tbl_current['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($tbl_current['size_index']) . ' in index.</p>';
2929 + $out3 .= '<pre>' . $tbl_current['schema'] . '</pre>';
2930 + $out3 .= '</td>';
2931 + $out3 .= '<td>';
2932 + $out3 .= '<p>' . number_format($tbl_snapshot['rows']) . ' rows totaling ' . WP_Reset_Utility::format_size($tbl_snapshot['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($tbl_snapshot['size_index']) . ' in index.</p>';
2933 + $out3 .= '<pre>' . $tbl_snapshot['schema'] . '</pre>';
2934 + $out3 .= '</td>';
2935 + $out3 .= '</tr>';
2936 + $out3 .= '</table>';
2937 + $out3 .= '</div>';
2938 + } elseif ($schema1 != $schema2) {
2939 + require_once $this->plugin_dir . 'libs/diff.php';
2940 + require_once $this->plugin_dir . 'libs/diff/Renderer/Html/SideBySide.php';
2941 + $diff = new WPR_Diff(explode("\n", $tbl_current['schema']), explode("\n", $tbl_snapshot['schema']), array('ignoreWhitespace' => false));
2942 + $renderer = new WPR_Diff_Renderer_Html_SideBySide;
1761 2943
2944 + $out2 .= '<div class="wpr-table-container" data-table="' . $tbl_current['basename'] . '">';
2945 + $out2 .= '<table>';
2946 + $out2 .= '<tr title="Click to show/hide more info" class="wpr-table-difference header-row">';
2947 + $out2 .= '<td><b>' . $tbl_current['fullname'] . '</b> table schemas do not match</td>';
2948 + $out2 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b> table schemas do not match<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2949 + $out2 .= '</tr>';
2950 + $out2 .= '<tr class="hidden">';
2951 + $out2 .= '<td>';
2952 + $out2 .= '<p>' . number_format($tbl_current['rows']) . ' rows totaling ' . WP_Reset_Utility::format_size($tbl_current['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($tbl_current['size_index']) . ' in index.</p>';
2953 + $out2 .= '</td>';
2954 + $out2 .= '<td>';
2955 + $out2 .= '<p>' . number_format($tbl_snapshot['rows']) . ' rows totaling ' . WP_Reset_Utility::format_size($tbl_snapshot['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($tbl_snapshot['size_index']) . ' in index.</p>';
2956 + $out2 .= '</td>';
2957 + $out2 .= '</tr>';
2958 + $out2 .= '<tr class="hidden">';
2959 + $out2 .= '<td colspan="2" class="no-padding">';
2960 + $out2 .= $diff->Render($renderer);
2961 + $out2 .= '</td>';
2962 + $out2 .= '</tr>';
2963 + $out2 .= '</table>';
2964 + $out2 .= '</div>';
2965 + } else {
2966 + $out2 .= '<div class="wpr-table-container" data-table="' . $tbl_current['basename'] . '">';
2967 + $out2 .= '<table>';
2968 + $out2 .= '<tr title="Click to show/hide more info" class="wpr-table-difference header-row">';
2969 + $out2 .= '<td><b>' . $tbl_current['fullname'] . '</b> data in tables does not match</td>';
2970 + $out2 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b> data in tables does not match<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2971 + $out2 .= '</tr>';
2972 + $out2 .= '<tr class="hidden">';
2973 + $out2 .= '<td>';
2974 + $out2 .= '<p>' . number_format($tbl_current['rows']) . ' rows totaling ' . WP_Reset_Utility::format_size($tbl_current['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($tbl_current['size_index']) . ' in index.</p>';
2975 + $out2 .= '</td>';
2976 + $out2 .= '<td>';
2977 + $out2 .= '<p>' . number_format($tbl_snapshot['rows']) . ' rows totaling ' . WP_Reset_Utility::format_size($tbl_snapshot['size_data']) . ' in data and ' . WP_Reset_Utility::format_size($tbl_snapshot['size_index']) . ' in index.</p>';
2978 + $out2 .= '</td>';
2979 + $out2 .= '</tr>';
1762 2980
1763 - /**
1764 - * Add single plugin to featured list
1765 - *
1766 - * @return object
1767 - */
1768 - function add_plugin_featured($plugin_slug, $res) {
1769 - // check if plugin is alredy on the list
1770 - if (is_array($res->plugins)) {
1771 - foreach ($res->plugins as $plugin) {
1772 - if ($plugin->slug == $plugin_slug) {
1773 - return $res;
2981 + $out2 .= '<tr class="hidden">';
2982 + $out2 .= '<td colspan="2">';
2983 + if ($tbl_current['corename'] == 'options') {
2984 + $ss_prefix = $tbl_snapshot['uid'] . '_' . $wpdb->prefix;
2985 + //Due to the working with various table sets, not always prefixed with the WordPress Database Table prefix we need to use custom unprepared queries
2986 + $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;"); //phpcs:ignore
2987 + $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;"); //phpcs:ignore
2988 + $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;"); //phpcs:ignore
2989 + $out2 .= '<table class="table_diff">';
2990 + $out2 .= '<tr><td style="width: 100px;"><b>Option Name</b></td><td><b>Current Value</b></td><td><b>Snapshot Value</b></td></tr>';
2991 + foreach ($diff_rows as $row) {
2992 + $out2 .= '<tr>';
2993 + $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
2994 + $out2 .= '<td>' . (empty($row->current_value) ? '<i>empty</i>' : $row->current_value) . '</td>';
2995 + $out2 .= '<td>' . (empty($row->snapshot_value) ? '<i>empty</i>' : $row->snapshot_value) . '</td>';
2996 + $out2 .= '</tr>';
2997 + } // foreach
2998 + foreach ($only_current as $row) {
2999 + $out2 .= '<tr>';
3000 + $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
3001 + $out2 .= '<td>' . (empty($row->current_value) ? '<i>empty</i>' : $row->current_value) . '</td>';
3002 + $out2 .= '<td><i>not found in snapshot</i></td>';
3003 + $out2 .= '</tr>';
3004 + } // foreach
3005 + foreach ($only_current as $row) {
3006 + $out2 .= '<tr>';
3007 + $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
3008 + $out2 .= '<td><i>not found in current tables</i></td>';
3009 + $out2 .= '<td>' . (empty($row->snapshot_value) ? '<i>empty</i>' : $row->snapshot_value) . '</td>';
3010 + $out2 .= '</tr>';
3011 + } // foreach
3012 + $out2 .= '</table>';
3013 + } else {
3014 + $out2 .= '<p class="textcenter">Detailed data diff is not available for this table.</p>';
3015 + }
3016 + $out2 .= '</td>';
3017 + $out2 .= '</tr>';
3018 +
3019 + $out2 .= '</table>';
3020 + $out2 .= '</div>';
3021 + }
3022 + } // foreach in both
3023 +
3024 + return $out . $out2 . $out3;
3025 + } // do_compare_snapshots
3026 +
3027 +
3028 + /**
3029 + * Generates a unique snapshot ID; verified non-existing
3030 + *
3031 + * @return string
3032 + */
3033 + function generate_snapshot_uid($length = 6)
3034 + {
3035 + global $wpdb;
3036 + $snapshots = $this->get_snapshots();
3037 + $cnt = 0;
3038 + $uid = false;
3039 +
3040 + do {
3041 + $cnt++;
3042 + $uid = substr(str_shuffle(str_repeat('abcdefghijklmnopqrstuvwxyz', $length)), 0, $length);
3043 + // phpcs:ignore db call warning as we are using uncommon queries
3044 + $verify_db = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array('%' . $uid . '%'))); // phpcs:ignore
3045 + } while (!empty($verify_db) && isset($snapshots[$uid]) && $cnt < 30);
3046 +
3047 + if ($cnt == 30) {
3048 + $uid = false;
1774 3049 }
1775 - } // foreach
1776 - }
1777 3050
1778 - if ($plugin_info = get_transient('wf-plugin-info-' . $plugin_slug)) {
1779 - $tmp1 = array_slice($res->plugins, 0, 2, false);
1780 - $tmp2 = array_slice($res->plugins, 2, sizeof($res->plugins) - 2, false);
1781 - $res->plugins = array_merge($tmp1, array($plugin_info), $tmp2);
1782 - } else {
1783 - $plugin_info = plugins_api('plugin_information', array(
1784 - 'slug' => $plugin_slug,
1785 - 'is_ssl' => is_ssl(),
1786 - 'fields' => array(
1787 - 'banners' => true,
1788 - 'reviews' => true,
1789 - 'downloaded' => true,
1790 - 'active_installs' => true,
1791 - 'icons' => true,
1792 - 'short_description' => true,
1793 - )
1794 - ));
1795 - if (!is_wp_error($plugin_info)) {
1796 - $tmp1 = array_slice($res->plugins, 0, 2, false);
1797 - $tmp2 = array_slice($res->plugins, 2, sizeof($res->plugins) - 2, false);
1798 - $res->plugins = array_merge($tmp1, array($plugin_info), $tmp2);
1799 - set_transient('wf-plugin-info-' . $plugin_slug, $plugin_info, DAY_IN_SECONDS * 7);
1800 - }
1801 - }
3051 + return $uid;
3052 + } // generate_snapshot_uid
1802 3053
1803 - return $res;
1804 - } // add_plugin_featured
1805 3054
3055 + // auto download / install / activate WP Force SSL plugin
3056 + function install_wpfssl()
3057 + {
3058 + check_ajax_referer('install_wpfssl');
1806 3059
1807 - /**
1808 - * Add plugins to featured plugins list
1809 - *
1810 - * @return object
1811 - */
1812 - function plugins_api_result($res, $action, $args) {
1813 - remove_filter('plugins_api_result', array($this, 'plugins_api_result'), 10, 3);
3060 + if (false === current_user_can('manage_options')) {
3061 + wp_die('Sorry, you have to be an admin to run this action.');
3062 + }
1814 3063
1815 - $res = $this->add_plugin_featured('security-ninja', $res);
3064 + $plugin_slug = 'wp-force-ssl/wp-force-ssl.php';
3065 + $plugin_zip = 'https://downloads.wordpress.org/plugin/wp-force-ssl.latest-stable.zip';
1816 3066
1817 - return $res;
1818 - } // plugins_api_result
3067 + @include_once ABSPATH . 'wp-admin/includes/plugin.php';
3068 + @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
3069 + @include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
3070 + @include_once ABSPATH . 'wp-admin/includes/file.php';
3071 + @include_once ABSPATH . 'wp-admin/includes/misc.php';
3072 + echo '<style>
3073 + body{
3074 + font-family: sans-serif;
3075 + font-size: 14px;
3076 + line-height: 1.5;
3077 + color: #444;
3078 + }
3079 + </style>';
1819 3080
3081 + echo '<div style="margin: 20px; color:#444;">';
3082 + echo 'If things are not done in a minute <a target="_parent" href="' . esc_url(admin_url('plugin-install.php?s=force%20ssl%20webfactory&tab=search&type=term')) . '">install the plugin manually via Plugins page</a><br><br>';
3083 + echo 'Starting ...<br><br>';
1820 3084
1821 - /**
1822 - * Clean up on uninstall; no action on deactive at the moment
1823 - *
1824 - * @return null
1825 - */
1826 - static function uninstall() {
1827 - delete_option('wp-reset');
1828 - delete_option('wp-reset-snapshots');
1829 - } // uninstall
3085 + wp_cache_flush();
3086 + $upgrader = new Plugin_Upgrader();
3087 + echo 'Check if WP Force SSL is already installed ... <br />';
3088 + if ($this->is_plugin_installed($plugin_slug)) {
3089 + echo 'WP Force SSL is already installed! <br /><br />Making sure it\'s the latest version.<br />';
3090 + $upgrader->upgrade($plugin_slug);
3091 + $installed = true;
3092 + } else {
3093 + echo 'Installing WP Force SSL.<br />';
3094 + $installed = $upgrader->install($plugin_zip);
3095 + }
3096 + wp_cache_flush();
1830 3097
3098 + if (!is_wp_error($installed) && $installed) {
3099 + echo 'Activating WP Force SSL.<br />';
3100 + $activate = activate_plugin($plugin_slug);
1831 3101
1832 - /**
1833 - * Disabled; we use singleton pattern so magic functions need to be disabled
1834 - *
1835 - * @return null
1836 - */
1837 - private function __clone() {}
3102 + if (is_null($activate)) {
3103 + echo 'WP Force SSL Activated.<br />';
1838 3104
3105 + echo '<script>setTimeout(function() { top.location = "tools.php?page=wp-reset"; }, 1000);</script>';
3106 + echo '<br>If you are not redirected in a few seconds - <a href="tools.php?page=wp-reset" target="_parent">click here</a>.';
3107 + }
3108 + } else {
3109 + echo 'Could not install WP Force SSL. You\'ll have to <a target="_parent" href="' . esc_url(admin_url('plugin-install.php?s=force%20ssl%20webfactory&tab=search&type=term')) . '">download and install manually</a>.';
3110 + }
1839 3111
1840 - /**
1841 - * Disabled; we use singleton pattern so magic functions need to be disabled
1842 - *
1843 - * @return null
1844 - */
1845 - private function __sleep() {}
3112 + echo '</div>';
3113 + } // install_wpfssl
1846 3114
3115 + /**
3116 + * Initializes the WordPress filesystem.
3117 + *
3118 + * @return bool
3119 + */
3120 + function wp_init_filesystem()
3121 + {
3122 + if (! $this->filesystem_initialized) {
3123 + if (! class_exists('WP_Filesystem')) {
3124 + require_once ABSPATH . 'wp-admin/includes/file.php';
3125 + }
1847 3126
1848 - /**
1849 - * Disabled; we use singleton pattern so magic functions need to be disabled
1850 - *
1851 - * @return null
1852 - */
1853 - private function __wakeup() {}
3127 + WP_Filesystem();
3128 + $this->filesystem_initialized = true;
3129 + }
3130 +
3131 + return true;
3132 + }
3133 +
3134 + /**
3135 + * Clean up on uninstall; no action on deactive at the moment
3136 + *
3137 + * @return null
3138 + */
3139 + static function uninstall()
3140 + {
3141 + delete_option('wp-reset');
3142 + delete_option('wp-reset-snapshots');
3143 + } // uninstall
3144 +
3145 +
3146 + /**
3147 + * Disabled; we use singleton pattern so magic functions need to be disabled
3148 + *
3149 + * @return null
3150 + */
3151 + function __clone() {}
3152 +
3153 +
3154 + /**
3155 + * Disabled; we use singleton pattern so magic functions need to be disabled
3156 + *
3157 + * @return null
3158 + */
3159 + function __sleep() {}
3160 +
3161 +
3162 + /**
3163 + * Disabled; we use singleton pattern so magic functions need to be disabled
3164 + *
3165 + * @return null
3166 + */
3167 + function __wakeup() {}
1854 3168 } // WP_Reset class
1855 3169
1856 3170
1857 3171 // Create plugin instance and hook things up
1858 -global $wp_reset;
1859 -$wp_reset = WP_Reset::getInstance();
1860 -add_action('plugins_loaded', array($wp_reset, 'load_textdomain'));
1861 -register_uninstall_hook(__FILE__, array('WP_Reset', 'uninstall'));
3172 +// Only if in admin - plugin has no frontend functionality
3173 +if (is_admin() || WP_Reset::is_cli_running()) {
3174 + global $wp_reset;
3175 + $wp_reset = WP_Reset::getInstance();
3176 + add_action('plugins_loaded', array($wp_reset, 'load_textdomain'));
3177 + register_uninstall_hook(__FILE__, array('WP_Reset', 'uninstall'));
3178 +}