PluginProbe
WP Reset / 2.08
WP Reset v2.08
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
wp-reset / wp-reset.php

wp-reset.php in WP Reset 2.08, at wp-reset.php

3,198 lines 164.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: WP Reset
4 Plugin URI: https://wpreset.com/
5 Description: Reset the entire site or just selected parts while reserving the option to undo by using snapshots.
6 Version: 2.08
7 Requires at least: 4.0
8 Requires PHP: 5.2
9 Tested up to: 7.1
10 License: GPLv2 or later
11 Author: WebFactory Ltd
12 Author URI: https://www.webfactoryltd.com/
13 Text Domain: wp-reset
14
15 Copyright 2015 - 2026 WebFactory Ltd (email: wpreset@webfactoryltd.com)
16
17 This program is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License, version 2, as
19 published by the Free Software Foundation.
20
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 */
30
31 // include only file
32 if (!defined('ABSPATH')) {
33 die('Do not open this file directly.');
34 }
35
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
45 // load WP-CLI commands, if needed
46 if (defined('WP_CLI') && WP_CLI) {
47 require_once dirname(__FILE__) . '/wp-reset-cli.php';
48 }
49
50
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;
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 }
75
76 return self::$instance;
77 } // getInstance
78
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();
91
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' => false,
100 'js_folder' => plugin_dir_url(__FILE__) . '/js/'
101 ));
102
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_wpcaptcha', array($this, 'install_wpcaptcha'));
111
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'));
115
116 $this->core_tables = array_map(function ($tbl) {
117 global $wpdb;
118 return $wpdb->prefix . $tbl;
119 }, $this->core_tables);
120 } // __construct
121
122
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');
131
132 return $plugin_data['version'];
133 } // get_plugin_version
134
135
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;
146
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 }
166
167 $this->options = $options;
168 return $options;
169 } // load_options
170
171
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
181
182
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'];
193
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 'wpcaptcha_install_url' => add_query_arg(array('action' => 'wpr_install_wpcaptcha', '_wpnonce' => wp_create_nonce('install_wpcaptcha'), '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
564 return true;
565 } // do_purge_cache
566
567
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;
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
581
582 do_action('wp_reset_reset_theme_options', $count);
583
584 return $count;
585 } // do_reset_theme_options
586
587
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;
600
601 if ( $wp_filesystem->is_dir( $upload_dir['basedir'] ) ) {
602
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 }
613
614 do_action('wp_reset_delete_uploads', $this->delete_count);
615
616 return $this->delete_count;
617 } // do_delete_uploads
618
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 }
634
635 $wp_reset_basename = plugin_basename(WP_RESET_FILE);
636 $params = shortcode_atts(array('keep_wp_reset' => true, 'silent_deactivate' => false), (array) $params);
637
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 }
644
645 if (!empty($active_plugins)) {
646 deactivate_plugins($active_plugins, $params['silent_deactivate'], false);
647 }
648
649 do_action('wp_reset_deactivate_plugins', $active_plugins, $params);
650
651 return sizeof($active_plugins);
652 } // do_deactivate_plugins
653
654
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 }
670
671 $wp_reset_basename = plugin_basename(WP_RESET_FILE);
672 $params = shortcode_atts(array('keep_wp_reset' => true), (array) $params);
673
674 $all_plugins = get_plugins();
675 if ($params['keep_wp_reset']) {
676 unset($all_plugins[$wp_reset_basename]);
677 }
678
679 if (!empty($all_plugins)) {
680 delete_plugins(array_keys($all_plugins));
681 }
682
683 do_action('wp_reset_delete_plugins', $all_plugins, $params);
684
685 return sizeof($all_plugins);
686 } // do_delete_plugins
687
688
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;
699
700 if (!function_exists('delete_theme')) {
701 require_once ABSPATH . 'wp-admin/includes/theme.php';
702 }
703
704 if (!function_exists('request_filesystem_credentials')) {
705 require_once ABSPATH . 'wp-admin/includes/file.php';
706 }
707
708 if (version_compare($wp_version, '5.0', '<') === true) {
709 $default_theme = 'twentyseventeen';
710 } else {
711 $default_theme = 'twentytwentyone';
712 }
713
714 $all_themes = wp_get_themes(array('errors' => null));
715
716 if (true == $keep_default_theme) {
717 unset($all_themes[$default_theme]);
718 }
719
720 foreach ($all_themes as $theme_slug => $theme_details) {
721 $res = delete_theme($theme_slug);
722 }
723
724 if (false == $keep_default_theme) {
725 update_option('template', '');
726 update_option('stylesheet', '');
727 update_option('current_theme', '');
728 }
729
730 do_action('wp_reset_delete_themes', $all_themes);
731
732 return sizeof($all_themes);
733 } // do_delete_themes
734
735
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();
745
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
752
753 do_action('wp_reset_truncate_custom_tables', $custom_tables);
754
755 return sizeof($custom_tables);
756 } // do_truncate_custom_tables
757
758
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();
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
775
776 do_action('wp_reset_drop_custom_tables', $custom_tables);
777
778 return sizeof($custom_tables);
779 } // do_drop_custom_tables
780
781
782 /**
783 * Delete .htaccess file
784 *
785 * @return bool|WP_Error Action status.
786 */
787 function do_delete_htaccess()
788 {
789 global $wp_filesystem;
790
791 if (empty($wp_filesystem)) {
792 require_once ABSPATH . '/wp-admin/includes/file.php';
793 WP_Filesystem();
794 }
795
796 $htaccess_path = $this->get_htaccess_path();
797 clearstatcache();
798
799 do_action('wp_reset_delete_htaccess', $htaccess_path);
800
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 }
804
805 if (!$wp_filesystem->is_writable($htaccess_path)) {
806 return new WP_Error(1, 'Htaccess file is not writable.');
807 }
808
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
815
816
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 }
827
828 if ($this->is_cli_running()) {
829 $_SERVER['SCRIPT_FILENAME'] = ABSPATH;
830 }
831
832 $filepath = get_home_path() . '.htaccess';
833
834 return $filepath;
835 } // get_htaccess_path
836
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');
846
847 if (!current_user_can('manage_options')) {
848 wp_send_json_error(__('You are not allowed to run this action.', 'wp-reset'));
849 }
850
851 if(!isset($_GET['tool'])){
852 wp_send_json_error(__('Unknown tool.', 'wp-reset'));
853 }
854
855 $tool = trim(sanitize_text_field(wp_unslash($_GET['tool'])));
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 }
862
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);
951
952 if (false !== ($error = get_transient('wf_install_error_' . $slug))) {
953 delete_transient('wf_install_error_' . $slug);
954 wp_send_json_success($error);
955 }
956
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']));
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';
979
980 wp_cache_flush();
981
982 $path = $this->get_plugin_path($slug);
983
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']));
1000
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
1014
1015
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
1031
1032
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;
1045
1046 // only admins can reset; double-check
1047 if (!$this->is_cli_running() && !current_user_can('manage_options')) {
1048 return false;
1049 }
1050
1051 // make sure the function is available to us
1052 if (!function_exists('wp_install')) {
1053 require ABSPATH . '/wp-admin/includes/upgrade.php';
1054 }
1055
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();
1063
1064 $active_plugins = get_transient('wpr_active_plugins');
1065 $active_theme = wp_get_theme();
1066
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 }
1075
1076 // delete custom tables with WP's prefix
1077 $prefix = str_replace('_', '\_', $wpdb->prefix);
1078
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
1081
1082 foreach ($tables as $table) {
1083 $wpdb->wpreset_table = $table;
1084 $wpdb->query("DROP TABLE " . $wpdb->wpreset_table); // phpcs:ignore
1085 }
1086
1087 $old_user_pass = $current_user->user_pass;
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'];
1092
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;
1097
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);
1103
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 }
1111
1112 $meta = $this->get_meta();
1113 $meta['reset_count']++;
1114 $this->update_options('meta', $meta);
1115
1116 // reactivate theme
1117 if (!empty($params['reactivate_theme'])) {
1118 switch_theme($active_theme->get_stylesheet());
1119 }
1120
1121 // reactivate WP Reset
1122 if (!empty($params['reactivate_wpreset'])) {
1123 activate_plugin(plugin_basename(__FILE__));
1124 }
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 }
1132
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);
1138
1139 wp_safe_redirect(admin_url() . '?wp-reset=success');
1140 exit;
1141 }
1142 } // do_reinstall
1143
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 }
1156
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 }
1160
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 }
1166
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 }
1172
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 }
1189
1190 $this->do_reinstall($params);
1191 }
1192 } // do_all_actions
1193
1194
1195 /**
1196 * Add action links 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>';
1205
1206 $upgrade_link = '<a href="' . esc_url(admin_url('tools.php?page=wp-reset#open-pro-dialog')) . '" title="' . esc_attr(__('Get WP Reset PRO', 'wp-reset')) . '"><b>' . esc_html(__('Get PRO', 'wp-reset')) . '</b></a>';
1207
1208 array_unshift($links, $upgrade_link);
1209 array_unshift($links, $settings_link);
1210
1211 return $links;
1212 } // plugin_action_links
1213
1214
1215 /**
1216 * Add links to plugin's description in plugins table
1217 *
1218 * @param array $links Initial list of links.
1219 * @param string $file Basename of current plugin.
1220 *
1221 * @return array
1222 */
1223 function plugin_meta_links($links, $file)
1224 {
1225 if ($file !== plugin_basename(__FILE__)) {
1226 return $links;
1227 }
1228
1229 $support_link = '<a target="_blank" href="https://wordpress.org/support/plugin/wp-reset" title="' . __('Get help', 'wp-reset') . '">' . __('Support', 'wp-reset') . '</a>';
1230 $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>';
1231 $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 �
1232 �
1233 �
1234 �
1235 �
1236 ', 'wp-reset') . '</a>';
1237
1238 $links[] = $support_link;
1239 $links[] = $home_link;
1240 $links[] = $rate_link;
1241
1242 return $links;
1243 } // plugin_meta_links
1244
1245
1246 /**
1247 * Test if we're on WPR's admin page
1248 *
1249 * @return bool
1250 */
1251 function is_plugin_page()
1252 {
1253 $current_screen = get_current_screen();
1254
1255 if (!empty($current_screen->id) && $current_screen->id == 'tools_page_wp-reset') {
1256 return true;
1257 } else {
1258 return false;
1259 }
1260 } // is_plugin_page
1261
1262
1263 /**
1264 * Add powered by text in admin footer
1265 *
1266 * @param string $text Default footer text.
1267 *
1268 * @return string
1269 */
1270 function admin_footer_text($text)
1271 {
1272 if (!$this->is_plugin_page()) {
1273 return $text;
1274 }
1275
1276 $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>�
1277 �
1278 �
1279 �
1280 �
1281 </span></a> to help us spread the word. Thank you from the WP Reset team!</i>';
1282
1283 return $text;
1284 } // admin_footer_text
1285
1286
1287 /**
1288 * Loads plugin's translated strings
1289 *
1290 * @return null
1291 */
1292 function load_textdomain()
1293 {
1294 load_plugin_textdomain('wp-reset');
1295 } // load_textdomain
1296
1297
1298 /**
1299 * Inform the user that WordPress has been successfully reset
1300 *
1301 * @return null
1302 */
1303 function notice_successful_reset()
1304 {
1305 global $current_user;
1306 /* translators: %1$s is replaced with the current user's username, %2$s is replaced with the URL to WP Reset settings page */
1307 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>');
1308
1309
1310 if (false == $this->get_dismissed_notices('rate')) {
1311 $request_url = isset($_SERVER['REQUEST_URI'])?sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])):'';
1312 $dismiss_url = add_query_arg(array('action' => 'wpr_dismiss_notice', 'notice' => 'rate', 'redirect' => urlencode($request_url)), admin_url('admin.php'));
1313 $dismiss_url = wp_nonce_url($dismiss_url, 'wpr_dismiss_notice');
1314
1315 echo '<p style="font-size: 14px;">';
1316 echo 'If WP Reset helped you please rate it so we can continue supporting it and helping others. Thank you!<br>';
1317 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>';
1318 echo '</p>';
1319 }
1320
1321 echo '</div>';
1322 } // notice_successful_reset
1323
1324
1325 /**
1326 * Generate a button that initiates snapshot creation
1327 *
1328 * @param string $tool_id Tool ID.
1329 * @param string $description Snapshot description.
1330 *
1331 * @return string
1332 */
1333 function get_snapshot_button($tool_id = '', $description = '')
1334 {
1335 $out = '';
1336 $out .= '<a data-tool-id="' . esc_attr($tool_id) . '" data-description="' . esc_attr($description) . '" class="button create-new-snapshot" href="#">Create snapshot</a>';
1337
1338 return $out;
1339 } // get_snapshot_button
1340
1341
1342 /**
1343 * Generate card header including title and action buttons
1344 *
1345 * @param string $title Card title.
1346 * @param string $card_id Card element #ID.
1347 * @param array $params Individual icons arguments
1348 *
1349 * @return string
1350 */
1351 function get_card_header($title, $card_id, $params = array())
1352 {
1353 $params = shortcode_atts(array(
1354 'documentation_link' => false,
1355 'iot_button' => false,
1356 'collapse_button' => false,
1357 'create_snapshot' => false,
1358 'pro' => false
1359 ), (array) $params);
1360
1361 if ($params['documentation_link'] === true) {
1362 $params['documentation_link'] = $card_id;
1363 }
1364
1365 $out = '';
1366 $out .= '<h4 id="' . esc_attr($card_id) . '"><span class="card-name">' . esc_html($title);
1367 if ($params['pro']) {
1368 $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>';
1369 }
1370 $out .= '</span>';
1371 $out .= '<div class="card-header-right">';
1372 if ($params['documentation_link']) {
1373 $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>';
1374 }
1375 if ($params['iot_button']) {
1376 $out .= '<a class="scrollto tooltip" href="#iot" title="Jump to Index of Tools"><span class="dashicons dashicons-screenoptions"></span></a>';
1377 }
1378 if ($params['create_snapshot']) {
1379 $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>';
1380 }
1381 if ($params['collapse_button']) {
1382 $out .= '<a class="toggle-card tooltip" href="#" title="' . __('Collapse / expand box', 'wp-reset') . '"><span class="dashicons dashicons-arrow-up-alt2"></span></a>';
1383 }
1384 $out .= '</div></h4>';
1385
1386 return $out;
1387 } // get_card_header
1388
1389
1390 /**
1391 * Generate tool icons and description detailing what it modifies
1392 *
1393 * @param bool $modify_files Does the tool modify files?
1394 * @param bool $modify_db Does the tool modify the database?
1395 * @param bool $plural Is there more than one tool in the set?
1396 *
1397 * @return string
1398 */
1399 function get_tool_icons($modify_files = false, $modify_db = false, $plural = false)
1400 {
1401 $out = '';
1402 $modify_files = (bool) $modify_files;
1403 $modify_db = (bool) $modify_db;
1404 $plural = (bool) $plural;
1405
1406 $out .= '<p class="tool-icons">';
1407 $out .= '<i class="icon-doc-text-inv' . ($modify_files ? ' red' : '') . '"></i> ';
1408 $out .= '<i class="icon-database' . ($modify_db ? ' red' : '') . '"></i> ';
1409
1410 if ($plural) {
1411 if ($modify_files && $modify_db) {
1412 $out .= 'these tools <b>modify files &amp; the database</b>';
1413 } elseif (!$modify_files && $modify_db) {
1414 $out .= 'these tools <b>modify the database</b> but they don\'t modify any files</b>';
1415 } elseif ($modify_files && !$modify_db) {
1416 $out .= 'these tools <b>modify files</b> but they don\'t modify the database</b>';
1417 }
1418 } else {
1419 if ($modify_files && $modify_db) {
1420 $out .= 'this tool <b>modifies files &amp; the database</b>';
1421 } elseif (!$modify_files && $modify_db) {
1422 $out .= 'this tool <b>modifies the database</b> but it doesn\'t modify any files</b>';
1423 } elseif ($modify_files && !$modify_db) {
1424 $out .= 'this tool <b>modifies files</b> but it doesn\'t modify the database</b>';
1425 } else {
1426 $out .= 'this tool doesn\'t modify files or the database';
1427 }
1428 }
1429 $out .= '</p>';
1430
1431 return $out;
1432 } // get_tool_icons
1433
1434
1435 /**
1436 * Outputs complete plugin's admin page
1437 *
1438 * @return null
1439 */
1440 function plugin_page()
1441 {
1442 // double check for admin privileges
1443 if (!current_user_can('manage_options')) {
1444 wp_die(esc_html__('Sorry, you are not allowed to access this page.', 'wp-reset'));
1445 }
1446
1447 echo '<div class="wrap">';
1448 echo '<form id="wp_reset_form" action="' . esc_url(admin_url('tools.php?page=wp-reset')) . '" method="post" autocomplete="off">';
1449
1450 echo '<header>';
1451 echo '<div class="wpr-container">';
1452 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') . '">';
1453 echo '</div>';
1454 echo '</header>';
1455
1456 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>';
1457
1458 echo '<div id="wp-reset-tabs" class="ui-tabs" style="display: none;">';
1459
1460 echo '<nav>';
1461 echo '<div class="wpr-container">';
1462 echo '<ul class="wpr-main-tab">';
1463 echo '<li><a href="#tab-reset">' . esc_html(__('Reset', 'wp-reset')) . '</a></li>';
1464 echo '<li><a href="#tab-tools">' . esc_html(__('Tools', 'wp-reset')) . '</a></li>';
1465 echo '<li><a href="#tab-snapshots">' . esc_html(__('Snapshots', 'wp-reset')) . '</a></li>';
1466 echo '<li><a href="#tab-collections">' . esc_html(__('Collections', 'wp-reset')) . '</a></li>';
1467 echo '<li><a href="#tab-support">' . esc_html(__('Support', 'wp-reset')) . '</a></li>';
1468 echo '<li class="tab-pro"><a href="#tab-pro">' . esc_html(__('PRO', 'wp-reset')) . '</a></li>';
1469 echo '</ul>';
1470 echo '</div>'; // container
1471 echo '</nav>';
1472
1473 echo '<div id="wpr-notifications">';
1474 echo '<div class="wpr-container">';
1475 $this->custom_notifications();
1476 echo '</div>';
1477 echo '</div>'; // wpr-notifications
1478
1479 // tabs
1480 echo '<div class="wpr-container">';
1481 echo '<div id="wpr-content">';
1482
1483 echo '<div style="display: none;" id="tab-reset">';
1484 $this->tab_reset();
1485 echo '</div>';
1486
1487 echo '<div style="display: none;" id="tab-tools">';
1488 $this->tab_tools();
1489 echo '</div>';
1490
1491 echo '<div style="display: none;" id="tab-snapshots">';
1492 $this->tab_snapshots();
1493 echo '</div>';
1494
1495 echo '<div style="display: none;" id="tab-collections">';
1496 $this->tab_collections();
1497 echo '</div>';
1498
1499 echo '<div style="display: none;" id="tab-support">';
1500 $this->tab_support();
1501 echo '</div>';
1502
1503 echo '<div style="display: none;" id="tab-pro">';
1504 $this->tab_pro();
1505 echo '</div>';
1506
1507 echo '</div>'; // content
1508 echo '</div>'; // container
1509 echo '</div>'; // wp-reset-tabs
1510
1511 echo '</form>';
1512
1513 echo '<div id="wpr-sidebar-ads">';
1514 echo '<div id="wpr-ad">';
1515 echo '<h3 class="textcenter"><b>Save time &amp; money with WP Reset PRO! First WP dev tool for non-devs.</b></h3>';
1516 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>';
1517 echo '<ul class="plain-list">
1518 <li>25+ Reset Tools</li>
1519 <li>Plugins &amp; Themes Collections</li>
1520 <li>Automatic Snapshots</li>
1521 <li>WP Reset Cloud, Dropbox &amp; Google Drive support</li>
1522 <li>Emergency Recovery Script</li>
1523 <li>White-label Mode + Complete Plugin Rebranding</li>
1524 <li>Licenses &amp; Sites Manager (remote SaaS dashboard)</li>
1525 <li>Friendly email support from plugin developers</li>
1526 </ul>';
1527 echo '<p class="textcenter"><a href="#" data-feature="sidebar-button" class="button-pro-feature button button-primary">Get PRO Now</a></p>';
1528 echo '</div>';
1529
1530 if (!defined('WPCAPTCHA_PLUGIN_FILE')) {
1531 echo '<div id="wpcaptcha-ad">';
1532 echo '<h3 class="textcenter"><b>Having problems with spam or bots? AI scapers giving you troubles?<br><br><u>Fix all your spam problems with one plugin!</u></b></h3>';
1533 echo '<p class="textcenter"><a href="#" class="textcenter install-wpcaptcha"><img style="max-width: 90%;" src="' . esc_url($this->plugin_url) . '/img/wp-captcha-logo.png" alt="Advanced Google ReCaptcha" title="Advanced Google ReCaptcha"></a></p>';
1534 echo '<p class="textcenter"><br><a href="#" class="install-wpcaptcha button button-primary">Install &amp; activate the free Google ReCaptcha plugin</a></p><p><a href="https://wordpress.org/plugins/advanced-google-recaptcha/" target="_blank">Advanced Google ReCaptcha</a> is a free WP plugin maintained by the same team as WP Reset. It has <b>+200,000 users, 5-star rating</b>, and is hosted on the official WP repository.</p>';
1535 echo '</div>';
1536 }
1537 echo '</div>';
1538
1539 echo '</div>'; // wrap
1540 } // plugin_page
1541
1542
1543 /**
1544 * Echoes all custom plugin notitications
1545 *
1546 * @return null
1547 */
1548 private function custom_notifications()
1549 {
1550 $notice_shown = false;
1551 $meta = $this->get_meta();
1552 $snapshots = $this->get_snapshots();
1553
1554 // update to PRO after activating the license
1555 if ($this->license->is_active()) {
1556 echo '<div class="card notice-wrapper notice-info">';
1557 echo '<h2>' . esc_html(__('Thank you for purchasing WP Reset PRO!', 'wp-reset')) . '</h2>';
1558 echo '<p>Your license has been verified &amp; activated.</b><br>To start using the PRO version, please follow these steps:';
1559 echo '<ol>';
1560 echo '<li><a href="https://dashboard.wpreset.com/pro-download/" target="_blank">Download</a> the latest version of the PRO plugin.</li>';
1561 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>';
1562 echo '<li>If asked to replace (overwrite) the free version - confirm it.</li>';
1563 echo '<li>Activate the plugin.</li>';
1564 echo '<li>That\'s it, no more steps.</li>';
1565 echo '</ol>';
1566 echo '</div>';
1567 $notice_shown = true;
1568 }
1569
1570 // warn that WPR is not WPMU compatible
1571 if (false === $notice_shown && is_multisite()) {
1572 echo '<div class="card notice-wrapper notice-error">';
1573 echo '<h2>' . esc_html__('WP Reset is not compatible with multisite!', 'wp-reset') . '</h2>';
1574 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>');
1575 echo '</div>';
1576 $notice_shown = true;
1577 }
1578
1579 // ask for review
1580 if ((!empty($meta['reset_count']) || !empty($snapshots) || current_time('timestamp', true) - $meta['first_install'] > DAY_IN_SECONDS)
1581 && false === $notice_shown
1582 && false == $this->get_dismissed_notices('rate')
1583 ) {
1584 echo '<div class="card notice-wrapper notice-info">';
1585 echo '<h2>' . esc_html__('Please help us spread the word &amp; keep the plugin up-to-date', 'wp-reset') . '</h2>';
1586 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>');
1587 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 �
1588 �
1589 �
1590 �
1591 �
1592 ', '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>';
1593 echo '</div>';
1594 $notice_shown = true;
1595 }
1596 } // custom_notifications
1597
1598
1599 /**
1600 * Echoes content for reset tab
1601 *
1602 * @return null
1603 */
1604 private function tab_reset()
1605 {
1606 global $current_user, $wpdb;
1607
1608 echo '<div class="card">';
1609 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Please read carefully before proceeding', 'wp-reset'), 'reset-description', array('collapse_button' => true)));
1610 echo '<div class="card-body">';
1611 echo '<p>The following table details what data will be deleted (reset or destroyed) when a selected reset tool is run. Please read it! ';
1612 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!';
1613 echo '</p>';
1614 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>';
1615 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>';
1616
1617 echo '<table id="reset-details" class="">';
1618 echo '<tr>';
1619 echo '<th>&nbsp;</th>';
1620 echo '<th>Options Reset<br><a data-feature="tool-options-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1621 echo '<th nowrap>Site Reset</th>';
1622 echo '<th>Nuclear Reset<br><a data-feature="tool-nuclear-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1623 echo '</tr>';
1624
1625 $rows = array();
1626 $rows['Posts, pages & custom post types'] = array(0, 1, 1);
1627 $rows['Comments'] = array(0, 1, 1);
1628 $rows['Media'] = array(0, 1, 1);
1629 $rows['Media files'] = array(0, 0, 1);
1630 $rows['Users'] = array(0, 1, 1);
1631 $rows['User roles'] = array(1, 1, 1);
1632 $rows['Current user - ' . $current_user->user_login] = array(0, 0, 0);
1633 $rows['Widgets'] = array(1, 1, 1);
1634 $rows['Transients'] = array(1, 1, 1);
1635 $rows['Settings &amp; options (from WP, plugins & themes)'] = array(1, 1, 1);
1636 $rows['Site title, WP address, site address,' . PHP_EOL . 'search engine visibility, timezone'] = array(0, 0, 0);
1637 $rows['Site language'] = array(0, 0, 1);
1638 $rows['Data in all default WP tables'] = array(0, 1, 1);
1639 $rows['Custom database tables with prefix ' . $wpdb->prefix] = array(0, 1, 1);
1640 $rows['Other database tables'] = array(0, 0, 0);
1641 $rows['Plugin files'] = array(0, 0, 1);
1642 $rows['MU plugin files'] = array(0, 0, 1);
1643 $rows['Drop-in files'] = array(0, 0, 1);
1644 $rows['Theme files'] = array(0, 0, 1);
1645 $rows['All files in uploads'] = array(0, 0, 1);
1646 $rows['Custom folders in wp-content'] = array(0, 0, 1);
1647
1648 foreach ($rows as $tool => $opt) {
1649 echo '<tr>';
1650 echo '<td>';
1651 WP_Reset_Utility::wp_kses_wf(nl2br(esc_html($tool)));
1652 echo '</td>';
1653 if (empty($opt[0])) {
1654 echo '<td><i class="dashicons dashicons-yes tooltip" title="Data will NOT be deleted, reset or modified"></i></td>';
1655 } else {
1656 echo '<td><i class="dashicons dashicons-trash red tooltip" title="Data WILL BE deleted, reset or modified"></i></td>';
1657 }
1658 if (empty($opt[1])) {
1659 echo '<td><i class="dashicons dashicons-yes tooltip" title="Data will NOT be deleted, reset or modified"></i></td>';
1660 } else {
1661 echo '<td><i class="dashicons dashicons-trash red tooltip" title="Data WILL BE deleted, reset or modified"></i></td>';
1662 }
1663 if (empty($opt[2])) {
1664 echo '<td><i class="dashicons dashicons-yes tooltip" title="Data will NOT be deleted, reset or modified"></i></td>';
1665 } else {
1666 echo '<td><i class="dashicons dashicons-trash red tooltip" title="Data WILL BE deleted, reset or modified"></i></td>';
1667 }
1668 echo '</tr>';
1669 } // foreach $rows
1670 echo '<tfoot>';
1671 echo '<tr>';
1672 echo '<th>&nbsp;</th>';
1673 echo '<th>Options Reset<br><a data-feature="tool-options-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1674 echo '<th nowrap>Site Reset</th>';
1675 echo '<th>Nuclear Reset<br><a data-feature="tool-nuclear-reset" class="pro-feature" href="#"><span class="pro">PRO</span> tool</a></th>';
1676 echo '</tr>';
1677 echo '</tfoot>';
1678 echo '</table>';
1679
1680 echo '<p><b>' . esc_html__('What happens when I run any Reset tool?', 'wp-reset') . '</b></p>';
1681 echo '<ul class="plain-list">';
1682 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>');
1683 echo '<li>' . esc_html__('you will have to confirm the action one more time', 'wp-reset') . '</li>';
1684 echo '<li>' . esc_html__('see the table above to find out what exactly will be reset or deleted', 'wp-reset') . '</li>';
1685 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>';
1686 echo '<li>' . esc_html__('you will be logged out, automatically logged back in and taken to the admin dashboard', 'wp-reset') . '</li>';
1687 echo '<li>' . esc_html__('WP Reset plugin will be reactivated if that option is chosen', 'wp-reset') . '</li>';
1688 echo '</ul>';
1689
1690 echo '<p><b>' . esc_html__('WP-CLI Support', 'wp-reset') . '</b><br>';
1691 /* translators: %s is replaced with the literal html "<code>wp help reset</code>" */
1692 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>');
1693 /* translators: %s is replaced with the literal html "<code>--yes</code>" */
1694 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>';
1695
1696 echo '</div></div>'; // card description
1697
1698 $theme = wp_get_theme();
1699 $theme_name = $theme->get('Name');
1700 if (empty($theme_name)) {
1701 $theme_name = '<i>no active theme</i>';
1702 }
1703 $active_plugins = get_option('active_plugins');
1704
1705 // nuclear reset
1706 echo '<div class="card">';
1707 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Nuclear Site Reset', 'wp-reset'), 'tool-nuclear-reset', array('collapse_button' => true, 'pro' => true)));
1708 echo '<div class="card-body">';
1709 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>';
1710
1711 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1712
1713 if (is_multisite()) {
1714 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>';
1715 } else {
1716 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>';
1717
1718 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>');
1719
1720 echo '<p class="mb0"><input id="nuclear_reset_confirm" type="text" placeholder="' . esc_html__('Type in "reset"', 'wp-reset') . '" value="" autocomplete="off"> &nbsp;';
1721 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>';
1722 }
1723 echo '</div>';
1724 echo '</div>'; // nuclear reset
1725
1726 // options reset
1727 echo '<div class="card">';
1728 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Options Reset', 'wp-reset'), 'tool-options-reset', array('collapse_button' => true, 'pro' => true)));
1729 echo '<div class="card-body">';
1730 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>';
1731
1732 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1733
1734 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>';
1735 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>';
1736
1737 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>';
1738 echo '</div>';
1739 echo '</div>'; // options reset
1740
1741 echo '<div class="card">';
1742 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Site Reset', 'wp-reset'), 'tool-site-reset', array('collapse_button' => true)));
1743 echo '<div class="card-body">';
1744 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>';
1745 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>';
1746
1747 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>';
1748 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>');
1749
1750 wp_nonce_field('wp-reset');
1751 /* translators: %s is replaced with "reset"*/
1752 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;';
1753 echo '<a id="wp_reset_submit" class="button button-delete">' . esc_html__('Reset Site', 'wp-reset') . '</a>';
1754 WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('reset-wordpress', 'Before resetting the site'));
1755 echo '</p>';
1756 echo '</div>';
1757 echo '</div>'; // card reset
1758 } // tab_reset
1759
1760
1761 /**
1762 * Echoes content for tools tab
1763 *
1764 * @return null
1765 */
1766 private function tab_tools()
1767 {
1768 global $wpdb, $wp_version, $wp_filesystem;
1769 $this->wp_init_filesystem();
1770
1771 $tools = array(
1772 'tool-reset-theme-options' => 'Reset Theme Options',
1773 '_tool-reset-user-roles' => 'Reset User Roles',
1774 'tool-delete-transients' => 'Delete Transients',
1775 'tool-purge-cache' => 'Purge Cache',
1776 'tool-delete-local-data' => 'Delete Local Data',
1777 '_tool-delete-content' => 'Delete Content',
1778 '_tool-delete-widgets' => 'Delete Widgets',
1779 'tool-delete-themes' => 'Delete Themes',
1780 'tool-delete-plugins' => 'Delete Plugins',
1781 '_tool-delete-mu-plugins-dropins' => 'Delete MU Plugins & Drop-ins',
1782 'tool-delete-uploads' => 'Clean uploads Folder',
1783 '_tool-delete-wp-content' => 'Clean wp-content Folder',
1784 'tool-empty-delete-custom-tables' => 'Empty or Delete Custom Tables',
1785 '_tool-switch-wp-version' => 'Switch WP Version',
1786 'tool-delete-htaccess' => 'Delete .htaccess File'
1787 );
1788
1789 echo '<div class="card">';
1790 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Index of Tools', 'wp-reset'), 'iot', array('collapse_button' => true)));
1791 echo '<div class="card-body">';
1792 $i = 0;
1793 $tools_nb = sizeof($tools);
1794 foreach ($tools as $tool_id => $tool_name) {
1795 if ($i == 0) {
1796 echo '<div class="third">';
1797 echo '<ul class="mb0 plain-list">';
1798 }
1799 if ($i == 5 || $i == 10) {
1800 echo '</div>';
1801 echo '<div class="third">';
1802 echo '<ul class="mb0 plain-list">';
1803 }
1804
1805 if ($tool_id[0] == '_') {
1806 $tool_id = ltrim($tool_id, '_');
1807 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>';
1808 } else {
1809 echo '<li><a title="Jump to ' . esc_attr($tool_name) . ' tool" class="scrollto" href="#' . esc_attr($tool_id) . '">' . esc_html($tool_name) . '</a></li>';
1810 }
1811
1812 if ($i == $tools_nb - 1) {
1813 echo '</ul>';
1814 echo '</div>'; // third
1815 }
1816 $i++;
1817 } // foreach tools
1818 echo '</div>';
1819 echo '</div>';
1820
1821 echo '<div class="card">';
1822 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)));
1823 echo '<div class="card-body">';
1824 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>');
1825 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1826 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>';
1827 WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('reset-theme-options', 'Before resetting theme options') . '</p>');
1828 echo '</div>';
1829 echo '</div>'; // reset theme options
1830
1831 echo '<div class="card default-collapsed">';
1832 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)));
1833 echo '<div class="card-body">';
1834 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>';
1835 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1836 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>';
1837 WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('reset-user-roles', 'Before resetting user roles') . '</p>');
1838 echo '</div>';
1839 echo '</div>'; // reset user roles
1840
1841 echo '<div class="card">';
1842 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Transients', 'wp-reset'), 'tool-delete-transients', array('iot_button' => true, 'collapse_button' => true)));
1843 echo '<div class="card-body">';
1844 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>';
1845 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1846 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>';
1847 WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('delete-transients', 'Before deleting transients') . '</p>');
1848 echo '</div>';
1849 echo '</div>'; // delete transients
1850
1851 echo '<div class="card">';
1852 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Purge Cache', 'wp-reset'), 'tool-purge-cache', array('collapse_button' => true, 'iot_button' => true)));
1853 echo '<div class="card-body">';
1854 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>';
1855 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1856 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>';
1857 echo '</div>';
1858 echo '</div>'; // purge cache
1859
1860 echo '<div class="card">';
1861 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)));
1862 echo '<div class="card-body">';
1863 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.
1864 </p>';
1865 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, false));
1866 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>';
1867 echo '</div>';
1868 echo '</div>'; // delete local data
1869
1870 echo '<div class="card default-collapsed">';
1871 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)));
1872 echo '<div class="card-body">';
1873 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>';
1874
1875 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1876
1877 $post_types = get_post_types('', false, 'and');
1878 $taxonomies = get_taxonomies('', false, 'and');
1879
1880 echo '<p><select size="6" multiple id="delete-content-types">';
1881 // phpcs:ignore db call warning as we are using uncommon queries
1882 echo '<option value="_comments">Comments (' . ((int) $wpdb->get_var("SELECT COUNT(comment_id) FROM $wpdb->comments")) . ')</option>'; // phpcs:ignore
1883 echo '<option value="_users">Users (' . ((int) $wpdb->get_var("SELECT COUNT(id) FROM $wpdb->users")) . ')</option>'; // phpcs:ignore
1884 foreach ($post_types as $type) {
1885 $count = wp_count_posts($type->name, 'readable');
1886 $tmp = 0;
1887 foreach ($count as $cnt) {
1888 $tmp += (int) $cnt;
1889 }
1890 echo '<option value="' . esc_attr($type->name) . '">Post type - ' . esc_html($type->label . ' (' . $tmp) . ')</option>';
1891 } // foreach post types
1892 foreach ($taxonomies as $tax) {
1893 echo '<option value="_tax_' . esc_attr($tax->name) . '">Taxonomy - ' . esc_html($tax->label . ' (' . wp_count_terms($tax->name)) . ')</option>';
1894 } // foreach post types
1895
1896 echo '</select><br>';
1897 echo 'Select content object(s) you want to delete. Use ctrl + click to select multiple objects.</p>';
1898
1899 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>';
1900 echo '</div>';
1901 echo '</div>'; // delete content
1902
1903 echo '<div class="card default-collapsed">';
1904 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)));
1905 echo '<div class="card-body">';
1906 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>';
1907
1908 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true));
1909
1910 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>';
1911 echo '</div>';
1912 echo '</div>'; // delete widgets
1913
1914 $theme = wp_get_theme();
1915
1916 echo '<div class="card">';
1917 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Themes', 'wp-reset'), 'tool-delete-themes', array('iot_button' => true, 'collapse_button' => true)));
1918 echo '<div class="card-body">';
1919 /* translators: %s is replaced with the currently active theme name */
1920 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>');
1921 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1922 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>';
1923 echo '</div>';
1924 echo '</div>'; // delete themes
1925
1926 echo '<div class="card">';
1927 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Delete Plugins', 'wp-reset'), 'tool-delete-plugins', array('iot_button' => true, 'collapse_button' => true)));
1928 echo '<div class="card-body">';
1929 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>');
1930 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
1931 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>';
1932 echo '</div>';
1933 echo '</div>'; // delete plugins
1934
1935 echo '<div class="card default-collapsed">';
1936 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)));
1937 echo '<div class="card-body">';
1938 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>';
1939 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>';
1940
1941 if (is_multisite()) {
1942 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>';
1943 } else {
1944 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false, true));
1945 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>';
1946 }
1947 echo '</div>';
1948 echo '</div>'; // delete MU plugins and dropins
1949
1950 $upload_dir = wp_upload_dir(gmdate('Y/m'), true);
1951 $upload_dir['basedir'] = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $upload_dir['basedir']);
1952
1953 echo '<div class="card">';
1954 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)));
1955 echo '<div class="card-body">';
1956 /* translators: %1$s is replaced with the uploads folder path, %2$s is replaced with the URL to WordPress Media upload page */
1957 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>');
1958 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false));
1959 if (false != $upload_dir['error']) {
1960 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>';
1961 } else {
1962 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>';
1963 }
1964 echo '</div>';
1965 echo '</div>'; // clean uploads folder
1966
1967 echo '<div class="card default-collapsed">';
1968 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)));
1969 echo '<div class="card-body">';
1970 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>';
1971 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false));
1972 if (false === $wp_filesystem->is_writable(trailingslashit(WP_CONTENT_DIR))) {
1973 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>';
1974 } else {
1975 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>';
1976 }
1977 echo '</div>';
1978 echo '</div>'; // clean wp-content
1979
1980 $custom_tables = $this->get_custom_tables();
1981
1982 echo '<div class="card">';
1983 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)));
1984 echo '<div class="card-body">';
1985 /* translators: %s is replaced with the table prefix */
1986 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)));
1987 if ($custom_tables) {
1988 /* translators: %d is replaced with the number of custom tables */
1989 WP_Reset_Utility::wp_kses_wf('<p>' . sprintf(__('The following %d custom tables are affected by this tool: ', 'wp-reset'), sizeof($custom_tables)));
1990 foreach ($custom_tables as $tbl) {
1991 echo '<code>' . esc_html($tbl['name']) . '</code>';
1992 if (next($custom_tables)) {
1993 echo ', ';
1994 }
1995 } // foreach
1996 echo '.</p>';
1997 $custom_tables_btns = '';
1998 } else {
1999 echo '<p>' . esc_html__('There are no custom tables. There\'s nothing for this tool to empty or delete.', 'wp-reset') . '</p>';
2000 $custom_tables_btns = ' disabled';
2001 }
2002 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(false, true, true));
2003 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>';
2004 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>';
2005 WP_Reset_Utility::wp_kses_wf($this->get_snapshot_button('drop-custom-tables', 'Before deleting custom tables'));
2006 echo '</p>';
2007 echo '</div>';
2008 echo '</div>'; // empty custom tables
2009
2010 echo '<div class="card default-collapsed">';
2011 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)));
2012 echo '<div class="card-body">';
2013 if (is_multisite()) {
2014 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>';
2015 } else {
2016 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>';
2017 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, true));
2018
2019 $wp_versions = WP_Reset_Utility::get_wordpress_versions();
2020 echo '<p><label for="select-wp-version">Select the WordPress version to switch to:</label> ';
2021 echo '<select id="select-wp-version">';
2022 echo '<option value="">select WordPress version</option>';
2023 foreach ($wp_versions as $version => $release_date) {
2024 if ($release_date == 'bleeding') {
2025 echo '<option value="bleeding">WordPress v' . esc_html($version) . ' (Bleeding edge nightly)' . ($wp_version == $version ? ' - installed' : '') . '</option>';
2026 } elseif ($release_date == 'point') {
2027 echo '<option value="point-' . esc_attr(substr($version, 0, 3)) . '">WordPress v' . esc_html($version) . ' (Point release nightly)' . ($wp_version == $version ? ' - installed' : '') . '</option>';
2028 } else {
2029 echo '<option value="' . esc_attr($version) . '">WordPress v' . esc_html($version) . ' (' . esc_attr(gmdate('Y-m-d', $release_date)) . ')' . ($wp_version == $version ? ' - installed' : '') . '</option>';
2030 }
2031 }
2032 echo '</select></p>';
2033
2034 echo '<p class="mb0">';
2035 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>';
2036 echo '</p>';
2037 }
2038 echo '</div>';
2039 echo '</div>'; // switch WP version
2040
2041 echo '<div class="card">';
2042 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)));
2043 echo '<div class="card-body">';
2044 /* translators: %s is replaced with the .htaccess file path */
2045 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())));
2046
2047 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>';
2048 WP_Reset_Utility::wp_kses_wf($this->get_tool_icons(true, false));
2049 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>';
2050
2051 echo '</div>';
2052 echo '</div>'; // delete htaccess
2053 } // tab_tools
2054
2055
2056 /**
2057 * Echoes content for collections tab
2058 *
2059 * @return null
2060 */
2061 private function tab_collections()
2062 {
2063 echo '<div class="card">';
2064 WP_Reset_Utility::wp_kses_wf($this->get_card_header('What are Plugin & Theme Collections?', 'collections-info', array('collapse_button' => false)));
2065 echo '<div class="card-body">';
2066 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>');
2067 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>';
2068 echo '</div>';
2069 echo '</div>'; // collections-info
2070
2071 $plugins = array();
2072 $plugins['eps-301-redirects'] = array('name' => '301 Redirects', 'desc' => 'Easiest way to manage redirects');
2073 $plugins['classic-editor'] = array('name' => 'Classic Editor', 'desc' => 'Any easy fix for all your Gutenberg caused troubles');
2074 $plugins['simple-author-box'] = array('name' => 'Simple Author Box', 'desc' => 'Simplest way to add responsive, great looking author boxes');
2075 $plugins['sticky-menu-or-anything-on-scroll'] = array('name' => 'Sticky Menu (or Anything!) on Scroll', 'desc' => 'Make any element on the page sticky.');
2076 $plugins['under-construction-page'] = array('name' => 'UnderConstructionPage', 'desc' => 'Working on your site? Put it in the under construction mode.');
2077 $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.');
2078
2079 echo '<div class="card" data-collection-id="1">';
2080 WP_Reset_Utility::wp_kses_wf($this->get_card_header('Must Have WordPress Plugins', 'collection-id-1', array('collapse_button' => false)));
2081 echo '<div class="card-body"><div class="thirdx2"><p class="_mb0"></p><div class="dropdown dropdown-right">
2082 <a class="button dropdown-toggle" href="#">Install collection</a>
2083 <div class="dropdown-menu">
2084 <a class="dropdown-item install-collection" data-activate="true" href="#">Install &amp; activate collection</a>
2085 <a class="dropdown-item install-collection" href="#">Install collection</a>
2086 <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>
2087 <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>
2088 </div>
2089 </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">
2090 <a class="button dropdown-toggle" href="#">Actions</a>
2091 <div class="dropdown-menu">
2092 <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>
2093 <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>
2094 <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>
2095 </div>
2096 </div><p></p></div><table class="collection-table"><tbody><tr><th>Type</th><th>Name &amp; Note</th><th class="actions">Actions</th></tr>';
2097 foreach ($plugins as $slug => $plugin) {
2098 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">
2099 <a class="button dropdown-toggle" href="#">Actions</a>
2100 <div class="dropdown-menu">
2101 <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>
2102 <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>
2103 <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>
2104 <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>
2105 </div>
2106 </div></td></tr>';
2107 } // foreach plugin
2108 echo '</tbody></table></div></div>';
2109 } // tab_collections
2110
2111
2112 /**
2113 * Echoes content for support tab
2114 *
2115 * @return null
2116 */
2117 private function tab_support()
2118 {
2119 echo '<div class="card">';
2120 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Documentation', 'wp-reset'), 'support-documentation', array('collapse_button' => false)));
2121 echo '<div class="card-body">';
2122 /* translators: %s is replaced with the link to the documentation page */
2123 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>');
2124 echo '</div>';
2125 echo '</div>'; // documentation
2126
2127 echo '<div class="card">';
2128 WP_Reset_Utility::wp_kses_wf($this->get_card_header('Emergency Recovery Script', 'support-ers', array('collapse_button' => false, 'pro' => true)));
2129 echo '<div class="card-body">';
2130 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>';
2131 echo '<ul class="plain-list">';
2132 echo '<li>Test the integrity of all WP core files and reinstall them if needed</li>';
2133 echo '<li>Detect and remove all files in core folders that are not a part of WP</li>';
2134 echo '<li>Deactivate and activate plugins without logging in to WP admin</li>';
2135 echo '<li>Deactivate and activate themes without logging in to WP admin</li>';
2136 echo '<li>Reset user privileges and roles</li>';
2137 echo '<li>Create new WP admin accounts without logging in to WP admin or knowing the admin username/password</li>';
2138 echo '<li>Modify WordPress address and site address</li>';
2139 echo '</ul>';
2140 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>';
2141 echo '</div>';
2142 echo '</div>'; // emergency recovery script
2143
2144 echo '<div class="card">';
2145 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Public Support Forum', 'wp-reset'), 'support-forum', array('collapse_button' => false)));
2146 echo '<div class="card-body">';
2147 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>');
2148 echo '</div>';
2149 echo '</div>'; // forum
2150
2151 echo '<div class="card">';
2152 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Premium Email Support', 'wp-reset'), 'support-email', array('collapse_button' => false, 'pro' => true)));
2153 echo '<div class="card-body">';
2154 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>';
2155 echo '</div>';
2156 echo '</div>'; // email support
2157
2158 echo '<div class="card">';
2159 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Care to Help Out?', 'wp-reset'), 'support-help-out', array('collapse_button' => false)));
2160 echo '<div class="card-body">';
2161 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>');
2162 echo '</div>';
2163 echo '</div>'; // help out
2164 } // tab_support
2165
2166
2167 /**
2168 * Echoes content for pro tab
2169 *
2170 * @return null
2171 */
2172 private function tab_pro()
2173 {
2174 $agency_lifetime = $this->generate_web_link('pricing-table', '/buy/', array('p' => 'wp-reset-pro-agency-ltd-launch'));
2175 $team_lifetime = $this->generate_web_link('pricing-table', '/', array(), 'pricing');
2176 $personal_lifetime = $this->generate_web_link('pricing-table', '/buy/', array('p' => 'wp-reset-pro-personal-ltd-launch'));
2177
2178 echo '<div class="card">';
2179 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('WP Reset PRO', 'wp-reset'), 'pro-features', array('collapse_button' => false)));
2180 echo '<div class="card-body">';
2181 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>';
2182 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>';
2183 echo '<p>Give WP Reset PRO a go. <b>It\'ll pay itself out in hours saved within the first few days!</b></p>';
2184 echo '<p>If you already have a PRO license, activate it below.</p>';
2185 echo '<p class="textcenter"><a href="#" data-feature="purchase-pro" class="button-pro-feature button button-delete">Get PRO now</a></p>';
2186 echo '</div>';
2187 echo '</div>';
2188
2189 echo '<div class="card">';
2190 WP_Reset_Utility::wp_kses_wf($this->get_card_header(__('Activate PRO License', 'wp-reset'), 'pro-activate', array('collapse_button' => false)));
2191 echo '<div class="card-body">';
2192
2193 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>
2194 <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>';
2195
2196 echo '<hr>';
2197 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">';
2198
2199 echo '<br><label>Status: </label>';
2200 if ($this->license->is_active()) {
2201 $license_formatted = $this->license->get_license_formatted();
2202 echo '<b style="color: #66b317;">Active</b><br>
2203 <label>Type: </label>' . esc_html($license_formatted['name_long']);
2204 echo '<br><label>Valid: </label>' . esc_html($license_formatted['valid_until']);
2205
2206 echo '<p>Thank you for purchasing WP Reset PRO! <b>Your license has been verified and activated.</b>';
2207 echo '<br>To start using the PRO version, please follow these steps:</p>';
2208 echo '<ol>';
2209 echo '<li><a href="https://dashboard.wpreset.com/pro-download/" target="_blank">Download</a> the latest version of the PRO plugin.</li>';
2210 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>';
2211 echo '<li>If asked to replace (overwrite) the free version - confirm it.</li>';
2212 echo '<li>Activate the plugin.</li>';
2213 echo '<li>That\'s it, no more steps.</li>';
2214 echo '</ol>';
2215 } else { // not active
2216 echo '<strong style="color: #ea1919;">Inactive</strong>';
2217 if (!empty($this->license->get_license('error'))) {
2218 echo '<br><label>Error: </label>' . esc_html($this->license->get_license('error'));
2219 }
2220 }
2221 echo '</p>';
2222
2223 echo '<p>';
2224 if ($this->license->is_active()) {
2225 echo '<a href="#" id="wpr-save-license" data-text-wait="Validating. Please wait." class="button button-secondary">Save &amp; Revalidate License</a>';
2226 echo '&nbsp; &nbsp;<a href="#" id="wpr-deactivate-license" data-text-wait="Deactivating. Please wait." class="button button-delete">Deactivate License</a>';
2227 } else {
2228 echo '<a href="#" id="wpr-save-license" data-text-wait="Activating. Please wait." class="button button-primary">Save &amp; Activate License</a>';
2229 echo '&nbsp; &nbsp;<a href="#" data-text-wait="Activating. Please wait." class="button button-secondary" id="wpr-keyless-activation">Keyless Activation</a>';
2230 }
2231 echo '</p>';
2232 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>';
2233 echo '</p>';
2234
2235 echo '</div>';
2236 echo '</div>'; // activate PRO
2237
2238 WP_Reset_Utility::wp_kses_wf($this->pro_dialog());
2239 } // tab_pro
2240
2241
2242 function pro_dialog()
2243 {
2244 $out = '';
2245
2246 $out .= '<div id="wpreset-pro-dialog" style="display: none;" title="WP Reset PRO"><span class="ui-helper-hidden-accessible"><input type="text"/></span>';
2247
2248 $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>';
2249
2250 $out .= '<span>Limited PRO Offer - <b>all prices are LIFETIME</b>! Pay once &amp; use forever!</span>';
2251 $out .= '</div>';
2252
2253 $out .= '<table id="wpreset-pro-table">';
2254 $out .= '<tr>';
2255 $out .= '<td class="center">Lifetime Personal License</td>';
2256 $out .= '<td class="center">Lifetime Team License</td>';
2257 $out .= '<td class="center">Lifetime Agency License</td>';
2258 $out .= '</tr>';
2259
2260 $out .= '<tr class="prices">';
2261 $out .= '<td class="center"><del>$59 /year</del><br><span>$59</span> <b>/lifetime</b></td>';
2262 $out .= '<td class="center"><del>$79 /year</del><br><span>$69</span> <b>/lifetime</b></td>';
2263 $out .= '<td class="center"><del>$119 /year</del><br><span>$149</span> <b>/lifetime</b></td>';
2264 $out .= '</tr>';
2265
2266 $out .= '<tr>';
2267 $out .= '<td><span class="dashicons dashicons-yes"></span><b>1 Site License</b></td>';
2268 $out .= '<td><span class="dashicons dashicons-yes"></span><b>5 Sites License</b></td>';
2269 $out .= '<td><span class="dashicons dashicons-yes"></span><b>100 Sites License</b></td>';
2270 $out .= '</tr>';
2271
2272 $out .= '<tr>';
2273 $out .= '<td><span class="dashicons dashicons-yes"></span>All Plugin Features &amp; Tools</td>';
2274 $out .= '<td><span class="dashicons dashicons-yes"></span>All Plugin Features &amp; Tools</td>';
2275 $out .= '<td><span class="dashicons dashicons-yes"></span>All Plugin Features &amp; Tools</td>';
2276 $out .= '</tr>';
2277
2278 $out .= '<tr>';
2279 $out .= '<td><span class="dashicons dashicons-yes"></span>Lifetime Updates &amp; Support</td>';
2280 $out .= '<td><span class="dashicons dashicons-yes"></span>Lifetime Updates &amp; Support</td>';
2281 $out .= '<td><span class="dashicons dashicons-yes"></span>Lifetime Updates &amp; Support</td>';
2282 $out .= '</tr>';
2283
2284 $out .= '<tr>';
2285 $out .= '<td><span class="dashicons dashicons-yes"></span>2 GB WP Reset Cloud Storage</td>';
2286 $out .= '<td><span class="dashicons dashicons-yes"></span>10 GB WP Reset Cloud Storage</td>';
2287 $out .= '<td><span class="dashicons dashicons-yes"></span>100 GB WP Reset Cloud Storage</td>';
2288 $out .= '</tr>';
2289
2290 $out .= '<tr>';
2291 $out .= '<td><span class="dashicons dashicons-yes"></span>Dropbox &amp; Google Drive support</td>';
2292 $out .= '<td><span class="dashicons dashicons-yes"></span>Dropbox &amp; Google Drive support</td>';
2293 $out .= '<td><span class="dashicons dashicons-yes"></span>Dropbox &amp; Google Drive support</td>';
2294 $out .= '</tr>';
2295
2296 $out .= '<tr>';
2297 $out .= '<td><span class="dashicons dashicons-yes"></span>Plugins & Themes Collections</td>';
2298 $out .= '<td><span class="dashicons dashicons-yes"></span>Plugins & Themes Collections</td>';
2299 $out .= '<td><span class="dashicons dashicons-yes"></span>Plugins & Themes Collections</td>';
2300 $out .= '</tr>';
2301
2302 $out .= '<tr>';
2303 $out .= '<td><span class="dashicons dashicons-yes"></span>Automatic Snapshots</td>';
2304 $out .= '<td><span class="dashicons dashicons-yes"></span>Automatic Snapshots</td>';
2305 $out .= '<td><span class="dashicons dashicons-yes"></span>Automatic Snapshots</td>';
2306 $out .= '</tr>';
2307
2308 $out .= '<tr>';
2309 $out .= '<td><span class="dashicons dashicons-yes"></span>Emergency Recovery Script</td>';
2310 $out .= '<td><span class="dashicons dashicons-yes"></span>Emergency Recovery Script</td>';
2311 $out .= '<td><span class="dashicons dashicons-yes"></span>Emergency Recovery Script</td>';
2312 $out .= '</tr>';
2313
2314 $out .= '<tr>';
2315 $out .= '<td><span class="dashicons dashicons-no"></span>Licenses &amp; Sites Manager</td>';
2316 $out .= '<td><span class="dashicons dashicons-yes"></span>Licenses &amp; Sites Manager</td>';
2317 $out .= '<td><span class="dashicons dashicons-yes"></span>Licenses &amp; Sites Manager</td>';
2318 $out .= '</tr>';
2319
2320 $out .= '<tr>';
2321 $out .= '<td><span class="dashicons dashicons-no"></span>White-label Mode</td>';
2322 $out .= '<td><span class="dashicons dashicons-yes"></span>White-label Mode</td>';
2323 $out .= '<td><span class="dashicons dashicons-yes"></span>White-label Mode</td>';
2324 $out .= '</tr>';
2325
2326 $out .= '<tr>';
2327 $out .= '<td><span class="dashicons dashicons-no"></span>Full Plugin Rebranding</td>';
2328 $out .= '<td><span class="dashicons dashicons-no"></span>Full Plugin Rebranding</td>';
2329 $out .= '<td><span class="dashicons dashicons-yes"></span>Full Plugin Rebranding</td>';
2330 $out .= '</tr>';
2331
2332 $out .= '<tr>';
2333 $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></td>';
2334 $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>';
2335 $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>';
2336 $out .= '</tr>';
2337
2338 $out .= '</table>';
2339
2340 $out .= '<div class="upsell-footer-2">Need the plugin only for a <b>short period of time</b>? <a class="button-buy" target="_blank" data-href-org="https://wpreset.com/buy2/?product=personal-monthly&ref=pricing-table" href="https://wpreset.com/buy2/?product=personal-monthly&ref=pricing-table"><b>Get it for ONLY $9.99</b><small> /month</small></a> &amp; cancel any time!</div>';
2341
2342 $out .= '<div class="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>';
2343
2344 return $out;
2345 } // pro_dialog
2346
2347
2348 /**
2349 * Echoes content for snapshots tab
2350 *
2351 * @return null
2352 */
2353 private function tab_snapshots()
2354 {
2355 global $wpdb;
2356 $tbl_core = $tbl_custom = $tbl_size = $tbl_rows = 0;
2357
2358 echo '<div class="card" id="card-snapshots">';
2359 echo '<h4>';
2360 echo esc_html__('Snapshots', 'wp-reset');
2361 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>';
2362 echo '</h4>';
2363 echo '<div class="card-body">';
2364 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>';
2365
2366 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>';
2367
2368 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>';
2369
2370 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>';
2371
2372 // phpcs:ignore db call warning as we are using uncommon queries
2373 $tables = $wpdb->get_results('SHOW TABLES', ARRAY_N); // phpcs:ignore
2374 if (is_array($tables)) {
2375 foreach ($tables as $table) {
2376 if (0 !== stripos($table[0], $wpdb->prefix)) {
2377 continue;
2378 }
2379
2380 if (in_array($table[0], $this->core_tables)) {
2381 $tbl_core++;
2382 } else {
2383 $tbl_custom++;
2384 }
2385 } // foreach
2386
2387 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 ';
2388 if ($tbl_custom) {
2389 echo esc_attr($tbl_custom) . ' custom table' . ($tbl_custom == 1 ? '' : 's');
2390 } else {
2391 echo 'no custom tables';
2392 }
2393 echo ' <span id="wpr-table-details"><a href="#" id="show-table-details">(show details)</a></span>';
2394 } else {
2395 echo '<b>Tables information is not available.</b> Something is not working properly on your site. Snapshots won\'t work.';
2396 }
2397
2398 echo '</div>';
2399 echo '</div>';
2400
2401 echo '<div class="card">';
2402 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)));
2403 echo '<div class="card-body">';
2404 if ($snapshots = $this->get_snapshots()) {
2405 $snapshots = array_reverse($snapshots);
2406 echo '<table id="wpr-snapshots">';
2407 echo '<tr><th>Date</th><th>Description</th><th class="ss-size">Size</th><th class="ss-actions">
2408 <div class="dropdown" style="font-weight:normal; margin-left: 4px;"><a class="button dropdown-toggle" href="#">Actions</a>
2409 <div class="dropdown-menu">
2410 <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>
2411 <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>
2412 </div>
2413 </div></th></tr>';
2414 foreach ($snapshots as $ss) {
2415 echo '<tr id="wpr-ss-' . esc_attr($ss['uid']) . '">';
2416 if (!empty($ss['name'])) {
2417 $name = $ss['name'];
2418 } else {
2419 $name = 'created on ' . gmdate(get_option('date_format'), strtotime($ss['timestamp'])) . ' @ ' . gmdate(get_option('time_format'), strtotime($ss['timestamp']));
2420 }
2421
2422 echo '<td>';
2423 if (current_time('timestamp') - strtotime($ss['timestamp']) > 12 * HOUR_IN_SECONDS) {
2424 echo esc_attr(gmdate(get_option('date_format'), strtotime($ss['timestamp']))) . '<br>@ ' . esc_attr(gmdate(get_option('time_format'), strtotime($ss['timestamp'])));
2425 } else {
2426 echo esc_attr(human_time_diff(strtotime($ss['timestamp']), current_time('timestamp'))) . ' ago';
2427 }
2428 echo '</td>';
2429
2430 echo '<td>';
2431 if (!empty($ss['name'])) {
2432 echo '<b>' . esc_html($ss['name']) . '</b><br>';
2433 }
2434 echo esc_attr($ss['tbl_core']) . ' standard &amp; ';
2435 if ($ss['tbl_custom']) {
2436 echo esc_attr($ss['tbl_custom']) . ' custom table' . ($ss['tbl_custom'] == 1 ? '' : 's');
2437 } else {
2438 echo 'no custom tables';
2439 }
2440 echo ' totaling ' . esc_attr(number_format($ss['tbl_rows'])) . ' rows</td>';
2441 echo '<td class="ss-size">' . esc_attr(WP_Reset_Utility::format_size($ss['tbl_size'])) . '</td>';
2442 echo '<td>';
2443 echo '<div class="dropdown">
2444 <a class="button dropdown-toggle" href="#">Actions</a>
2445 <div class="dropdown-menu">';
2446 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>';
2447 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>';
2448 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>';
2449 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>';
2450 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>';
2451 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>';
2452 echo '</div></div></td>';
2453 echo '</tr>';
2454 } // foreach
2455 echo '</table>';
2456 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>';
2457 } else {
2458 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>';
2459 }
2460 echo '</div>';
2461 echo '</div>';
2462
2463 echo '<div class="card">';
2464 WP_Reset_Utility::wp_kses_wf($this->get_card_header('Automatic Snapshots', 'snapshots-auto', array('collapse_button' => false, 'pro' => true)));
2465 echo '<div class="card-body">';
2466 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>
2467 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>';
2468 echo '</div>';
2469 echo '</div>';
2470 } // tab_snapshots
2471
2472
2473 /**
2474 * Helper function for generating links
2475 *
2476 * @param string $placement Optional. UTM content param.
2477 * @param string $page Optional. Page to link to.
2478 * @param array $params Optional. Extra URL params.
2479 * @param string $anchor Optional. URL anchor part.
2480 *
2481 * @return string
2482 */
2483 function generate_web_link($placement = '', $page = '/', $params = array(), $anchor = '')
2484 {
2485 $base_url = 'https://wpreset.com';
2486
2487 if ('/' != $page) {
2488 $page = '/' . trim($page, '/') . '/';
2489 }
2490 if ($page == '//') {
2491 $page = '/';
2492 }
2493
2494 if ($placement) {
2495 $placement = trim($placement);
2496 $placement = '-' . $placement;
2497 }
2498
2499 $parts = array_merge(array('ref' => 'wp-reset-free' . $placement), $params);
2500
2501 if (!empty($anchor)) {
2502 $anchor = '#' . trim($anchor, '#');
2503 }
2504
2505 $out = $base_url . $page . '?' . http_build_query($parts, '', '&amp;') . $anchor;
2506
2507 return $out;
2508 } // generate_web_link
2509
2510
2511 /**
2512 * Returns all saved snapshots from DB
2513 *
2514 * @return array
2515 */
2516 function get_snapshots()
2517 {
2518 $snapshots = get_option('wp-reset-snapshots', array());
2519
2520 return $snapshots;
2521 } // get_snapshots
2522
2523
2524 /**
2525 * Returns all custom table names, with prefix
2526 *
2527 * @return array
2528 */
2529 function get_custom_tables()
2530 {
2531 global $wpdb;
2532 $custom_tables = array();
2533
2534 // phpcs:ignore db call warning as we are using uncommon queries
2535 $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2536 if (is_array($table_status)) {
2537 foreach ($table_status as $index => $table) {
2538 if (0 !== stripos($table->Name, $wpdb->prefix)) {
2539 continue;
2540 }
2541 if (empty($table->Engine)) {
2542 continue;
2543 }
2544
2545 if (false === in_array($table->Name, $this->core_tables)) {
2546 $custom_tables[] = array('name' => $table->Name, 'rows' => $table->Rows, 'data_length' => $table->Data_length, 'index_length' => $table->Index_length);
2547 }
2548 } // foreach
2549 }
2550
2551 return $custom_tables;
2552 } // get_custom tables
2553
2554
2555 /**
2556 * Creates snapshot of current tables by copying them in the DB and saving metadata.
2557 *
2558 * @param int $name Optional. Name for the new snapshot.
2559 *
2560 * @return array|WP_Error Snapshot details in array on success, or error object on fail.
2561 */
2562 function do_create_snapshot($name = '')
2563 {
2564 global $wpdb;
2565 $snapshots = $this->get_snapshots();
2566 $snapshot = array();
2567 $uid = $this->generate_snapshot_uid();
2568 $tbl_core = $tbl_custom = $tbl_size = $tbl_rows = 0;
2569
2570 if (!$uid) {
2571 return new WP_Error(1, 'Unable to generate a valid snapshot UID.');
2572 }
2573
2574 if ($name) {
2575 $snapshot['name'] = substr(trim($name), 0, 64);
2576 } else {
2577 $snapshot['name'] = '';
2578 }
2579 $snapshot['uid'] = $uid;
2580 $snapshot['timestamp'] = current_time('mysql');
2581
2582 // phpcs:ignore db call warning as we are using uncommon queries
2583 $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2584 if (is_array($table_status)) {
2585 foreach ($table_status as $index => $table) {
2586 if (0 !== stripos($table->Name, $wpdb->prefix)) {
2587 continue;
2588 }
2589 if (empty($table->Engine)) {
2590 continue;
2591 }
2592
2593 $tbl_rows += $table->Rows;
2594 $tbl_size += $table->Data_length + $table->Index_length;
2595 if (in_array($table->Name, $this->core_tables)) {
2596 $tbl_core++;
2597 } else {
2598 $tbl_custom++;
2599 }
2600
2601 $wpdb->wpreset_snapshot_table_name = $table->Name;
2602 $wpdb->wpreset_snapshot_table_copy_name = $uid . '_' . $table->Name;
2603
2604 // phpcs:ignore db call warning as we are using uncommon queries
2605 $wpdb->query("OPTIMIZE TABLE " . $wpdb->wpreset_snapshot_table_name); // phpcs:ignore
2606 $wpdb->query("CREATE TABLE " . $wpdb->wpreset_snapshot_table_copy_name . " LIKE " . $wpdb->wpreset_snapshot_table_name); // phpcs:ignore
2607 $wpdb->query("INSERT " . $wpdb->wpreset_snapshot_table_copy_name . " SELECT * FROM " . $wpdb->wpreset_snapshot_table_name); // phpcs:ignore
2608 } // foreach
2609 } else {
2610 return new WP_Error(1, 'Can\'t get table status data.');
2611 }
2612
2613 $snapshot['tbl_core'] = $tbl_core;
2614 $snapshot['tbl_custom'] = $tbl_custom;
2615 $snapshot['tbl_rows'] = $tbl_rows;
2616 $snapshot['tbl_size'] = $tbl_size;
2617
2618
2619 $snapshots[$uid] = $snapshot;
2620 update_option('wp-reset-snapshots', $snapshots);
2621
2622 do_action('wp_reset_create_snapshot', $uid, $snapshot);
2623
2624 return $snapshot;
2625 } // create_snapshot
2626
2627
2628 /**
2629 * Delete snapshot metadata and tables from DB
2630 *
2631 * @param string $uid Snapshot unique 6-char ID.
2632 *
2633 * @return bool|WP_Error True on success, or error object on fail.
2634 */
2635 function do_delete_snapshot($uid = '')
2636 {
2637 global $wpdb;
2638 $snapshots = $this->get_snapshots();
2639
2640 if (strlen($uid) != 6) {
2641 return new WP_Error(1, 'Invalid UID format.');
2642 }
2643
2644 if (!isset($snapshots[$uid])) {
2645 return new WP_Error(1, 'Unknown snapshot ID.');
2646 }
2647
2648 // phpcs:ignore db call warning as we are using uncommon queries
2649 $tables = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array($uid . '\_%'))); // phpcs:ignore
2650 foreach ($tables as $table) {
2651 $wpdb->wpreset_snapshot_table = $table;
2652 $wpdb->query('DROP TABLE IF EXISTS ' . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2653 }
2654
2655 $snapshot_copy = $snapshots[$uid];
2656 unset($snapshots[$uid]);
2657 update_option('wp-reset-snapshots', $snapshots);
2658
2659 do_action('wp_reset_delete_snapshot', $uid, $snapshot_copy);
2660
2661 return true;
2662 } // delete_snapshot
2663
2664
2665 /**
2666 * Exports snapshot as SQL dump; saved in gzipped file in WP_CONTENT folder.
2667 *
2668 * @param string $uid Snapshot unique 6-char ID.
2669 *
2670 * @return string|WP_Error Export base filename, or error object on fail.
2671 */
2672 function do_export_snapshot($uid = '')
2673 {
2674 global $wp_filesystem;
2675 $this->wp_init_filesystem();
2676
2677 $snapshots = $this->get_snapshots();
2678
2679 if (strlen($uid) != 6) {
2680 return new WP_Error(1, 'Invalid snapshot ID format.');
2681 }
2682
2683 if (!isset($snapshots[$uid])) {
2684 return new WP_Error(1, 'Unknown snapshot ID.');
2685 }
2686
2687 require_once $this->plugin_dir . 'libs/dumper.php';
2688
2689 $snapshot_file_uid = md5($this->generate_snapshot_uid(10));
2690 try {
2691 $world_dumper = WPR_Shuttle_Dumper::create(array(
2692 'host' => DB_HOST,
2693 'username' => DB_USER,
2694 'password' => DB_PASSWORD,
2695 'db_name' => DB_NAME,
2696 ));
2697
2698 $folder = wp_mkdir_p(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder);
2699 if (!$folder) {
2700 return new WP_Error(1, 'Unable to create wp-content/' . $this->snapshots_folder . '/ folder.');
2701 }
2702
2703 $htaccess_content = 'AddType application/octet-stream .gz' . PHP_EOL;
2704 $htaccess_content .= 'Options -Indexes' . PHP_EOL;
2705
2706 $wp_filesystem->put_contents(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/.htaccess', $htaccess_content);
2707
2708 $world_dumper->dump(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/wp-reset-snapshot-' . $snapshot_file_uid . '.sql.gz', $uid . '_');
2709 } catch (Shuttle_Exception $e) {
2710 return new WP_Error(1, 'Couldn\'t create snapshot: ' . $e->getMessage());
2711 }
2712
2713 do_action('wp_reset_export_snapshot', 'wp-reset-snapshot-' . $snapshot_file_uid . '.sql.gz');
2714
2715 return 'wp-reset-snapshot-' . $snapshot_file_uid . '.sql.gz';
2716 } // export_snapshot
2717
2718
2719 /**
2720 * Replace current tables with ones in snapshot.
2721 *
2722 * @param string $uid Snapshot unique 6-char ID.
2723 *
2724 * @return bool|WP_Error True on success, or error object on fail.
2725 */
2726 function do_restore_snapshot($uid = '')
2727 {
2728 // phpcs:ignore db call warning as we are using uncommon queries
2729 global $wpdb;
2730 $new_tables = array();
2731 $snapshots = $this->get_snapshots();
2732
2733 if (($res = $this->verify_snapshot_integrity($uid)) !== true) {
2734 return $res;
2735 }
2736
2737 $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2738 if (is_array($table_status)) {
2739 foreach ($table_status as $index => $table) {
2740 if (0 !== stripos($table->Name, $uid . '_')) {
2741 continue;
2742 }
2743 if (empty($table->Engine)) {
2744 continue;
2745 }
2746
2747 $new_tables[] = $table->Name;
2748 } // foreach
2749 } else {
2750 return new WP_Error(1, 'Can\'t get table status data.');
2751 }
2752
2753 foreach ($table_status as $index => $table) {
2754 if (0 !== stripos($table->Name, $wpdb->prefix)) {
2755 continue;
2756 }
2757 if (empty($table->Engine)) {
2758 continue;
2759 }
2760
2761 $wpdb->wpreset_snapshot_table = $table->Name;
2762 $wpdb->query('DROP TABLE ' . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2763 } // foreach
2764
2765 // copy snapshot tables to original name
2766 foreach ($new_tables as $table) {
2767 $wpdb->wpreset_snapshot_table = $table;
2768 $wpdb->wpreset_snapshot_table_new = str_replace($uid . '_', '', $table);
2769
2770 $wpdb->query("CREATE TABLE " . $wpdb->wpreset_snapshot_table_new . " LIKE " . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2771 $wpdb->query("INSERT " . $wpdb->wpreset_snapshot_table_new . " SELECT * FROM " . $wpdb->wpreset_snapshot_table); // phpcs:ignore
2772 }
2773
2774 wp_cache_flush();
2775 update_option('wp-reset', $this->options);
2776 update_option('wp-reset-snapshots', $snapshots);
2777
2778 do_action('wp_reset_restore_snapshot', $uid);
2779
2780 return true;
2781 } // restore_snapshot
2782
2783
2784 /**
2785 * Verifies snapshot integrity by comparing metadata and data in DB
2786 *
2787 * @param string $uid Snapshot unique 6-char ID.
2788 *
2789 * @return bool|WP_Error True on success, or error object on fail.
2790 */
2791 function verify_snapshot_integrity($uid)
2792 {
2793 // phpcs:ignore db call warning as we are using uncommon queries
2794 global $wpdb;
2795 $tbl_core = $tbl_custom = 0;
2796 $snapshots = $this->get_snapshots();
2797
2798 if (strlen($uid) != 6) {
2799 return new WP_Error(1, 'Invalid snapshot ID format.');
2800 }
2801
2802 if (!isset($snapshots[$uid])) {
2803 return new WP_Error(1, 'Unknown snapshot ID.');
2804 }
2805
2806 $snapshot = $snapshots[$uid];
2807
2808 $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2809 if (is_array($table_status)) {
2810 foreach ($table_status as $index => $table) {
2811 if (0 !== stripos($table->Name, $uid . '_')) {
2812 continue;
2813 }
2814 if (empty($table->Engine)) {
2815 continue;
2816 }
2817
2818 if (in_array(str_replace($uid . '_', '', $table->Name), $this->core_tables)) {
2819 $tbl_core++;
2820 } else {
2821 $tbl_custom++;
2822 }
2823 } // foreach
2824
2825 if ($tbl_core != $snapshot['tbl_core'] || $tbl_custom != $snapshot['tbl_custom']) {
2826 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.');
2827 }
2828 } else {
2829 return new WP_Error(1, 'Can\'t get table status data.');
2830 }
2831
2832 return true;
2833 } // verify_snapshot_integrity
2834
2835
2836 /**
2837 * Compares a selected snapshot with the current table set in DB
2838 *
2839 * @param string $uid Snapshot unique 6-char ID.
2840 *
2841 * @return string|WP_Error Formatted table with details on success, or error object on fail.
2842 */
2843 function do_compare_snapshots($uid)
2844 {
2845 // phpcs:ignore db call warning as we are using uncommon queries
2846 global $wpdb;
2847 $current = $snapshot = array();
2848 $out = $out2 = $out3 = '';
2849
2850 if (($res = $this->verify_snapshot_integrity($uid)) !== true) {
2851 return $res;
2852 }
2853
2854 $table_status = $wpdb->get_results('SHOW TABLE STATUS'); // phpcs:ignore
2855 foreach ($table_status as $index => $table) {
2856 if (empty($table->Engine)) {
2857 continue;
2858 }
2859
2860 if (0 !== stripos($table->Name, $uid . '_') && 0 !== stripos($table->Name, $wpdb->prefix)) {
2861 continue;
2862 }
2863
2864 $info = array();
2865 $info['rows'] = $table->Rows;
2866 $info['size_data'] = $table->Data_length;
2867 $info['size_index'] = $table->Index_length;
2868 $wpdb->wpreset_table_name = $table->Name;
2869 $schema = $wpdb->get_row('SHOW CREATE TABLE ' . $wpdb->wpreset_table_name, ARRAY_N); // phpcs:ignore
2870 $info['schema'] = $schema[1];
2871 $info['engine'] = $table->Engine;
2872 $info['fullname'] = $table->Name;
2873 $basename = str_replace(array($uid . '_'), array(''), $table->Name);
2874 $info['basename'] = $basename;
2875 $info['corename'] = str_replace(array($wpdb->prefix), array(''), $basename);
2876 $info['uid'] = $uid;
2877
2878 if (0 === stripos($table->Name, $uid . '_')) {
2879 $snapshot[$basename] = $info;
2880 }
2881
2882 if (0 === stripos($table->Name, $wpdb->prefix)) {
2883 $info['uid'] = '';
2884 $current[$basename] = $info;
2885 }
2886 } // foreach
2887
2888 $in_both = array_keys(array_intersect_key($current, $snapshot));
2889 $in_current_only = array_diff_key($current, $snapshot);
2890 $in_snapshot_only = array_diff_key($snapshot, $current);
2891
2892 $out .= '<br><br>';
2893 foreach ($in_current_only as $table) {
2894 $out .= '<div class="wpr-table-container in-current-only" data-table="' . esc_attr($table['basename']) . '">';
2895 $out .= '<table>';
2896 $out .= '<tr title="Click to show/hide more info" class="wpr-table-missing header-row">';
2897 $out .= '<td><b>' . $table['fullname'] . '</b></td>';
2898 $out .= '<td>table is not present in snapshot<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2899 $out .= '</tr>';
2900 $out .= '<tr class="hidden">';
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 .= '<td>&nbsp;</td>';
2906 $out .= '</tr>';
2907 $out .= '</table>';
2908 $out .= '</div>';
2909 } // foreach in current only
2910
2911 foreach ($in_snapshot_only as $table) {
2912 $out .= '<div class="wpr-table-container in-snapshot-only" data-table="' . esc_attr($table['basename']) . '">';
2913 $out .= '<table>';
2914 $out .= '<tr title="Click to show/hide more info" class="wpr-table-missing header-row">';
2915 $out .= '<td>table is not present in current tables</td>';
2916 $out .= '<td><b>' . esc_html($table['fullname']) . '</b><span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2917 $out .= '</tr>';
2918 $out .= '<tr class="hidden">';
2919 $out .= '<td>&nbsp;</td>';
2920 $out .= '<td>';
2921 $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>';
2922 $out .= '<pre>' . $table['schema'] . '</pre>';
2923 $out .= '</td>';
2924 $out .= '</tr>';
2925 $out .= '</table>';
2926 $out .= '</div>';
2927 } // foreach in snapshot only
2928
2929 foreach ($in_both as $tablename) {
2930 $tbl_current = $current[$tablename];
2931 $tbl_snapshot = $snapshot[$tablename];
2932
2933 $schema1 = preg_replace('/(auto_increment=)([0-9]*) /i', '${1}1 ', $tbl_current['schema'], 1);
2934 $schema2 = preg_replace('/(auto_increment=)([0-9]*) /i', '${1}1 ', $tbl_snapshot['schema'], 1);
2935 $tbl_snapshot['tmp_schema'] = str_replace($tbl_snapshot['uid'] . '_' . $tablename, $tablename, $tbl_snapshot['schema']);
2936 $schema2 = str_replace($tbl_snapshot['uid'] . '_' . $tablename, $tablename, $schema2);
2937
2938 if ($tbl_current['rows'] == $tbl_snapshot['rows'] && $tbl_current['schema'] == $tbl_snapshot['tmp_schema']) {
2939 $out3 .= '<div class="wpr-table-container identical" data-table="' . esc_attr($tablename) . '">';
2940 $out3 .= '<table>';
2941 $out3 .= '<tr title="Click to show/hide more info" class="wpr-table-match header-row">';
2942 $out3 .= '<td><b>' . $tbl_current['fullname'] . '</b></td>';
2943 $out3 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b><span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2944 $out3 .= '</tr>';
2945 $out3 .= '<tr class="hidden">';
2946 $out3 .= '<td>';
2947 $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>';
2948 $out3 .= '<pre>' . $tbl_current['schema'] . '</pre>';
2949 $out3 .= '</td>';
2950 $out3 .= '<td>';
2951 $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>';
2952 $out3 .= '<pre>' . $tbl_snapshot['schema'] . '</pre>';
2953 $out3 .= '</td>';
2954 $out3 .= '</tr>';
2955 $out3 .= '</table>';
2956 $out3 .= '</div>';
2957 } elseif ($schema1 != $schema2) {
2958 require_once $this->plugin_dir . 'libs/diff.php';
2959 require_once $this->plugin_dir . 'libs/diff/Renderer/Html/SideBySide.php';
2960 $diff = new WPR_Diff(explode("\n", $tbl_current['schema']), explode("\n", $tbl_snapshot['schema']), array('ignoreWhitespace' => false));
2961 $renderer = new WPR_Diff_Renderer_Html_SideBySide;
2962
2963 $out2 .= '<div class="wpr-table-container" data-table="' . $tbl_current['basename'] . '">';
2964 $out2 .= '<table>';
2965 $out2 .= '<tr title="Click to show/hide more info" class="wpr-table-difference header-row">';
2966 $out2 .= '<td><b>' . $tbl_current['fullname'] . '</b> table schemas do not match</td>';
2967 $out2 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b> table schemas do not match<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2968 $out2 .= '</tr>';
2969 $out2 .= '<tr class="hidden">';
2970 $out2 .= '<td>';
2971 $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>';
2972 $out2 .= '</td>';
2973 $out2 .= '<td>';
2974 $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>';
2975 $out2 .= '</td>';
2976 $out2 .= '</tr>';
2977 $out2 .= '<tr class="hidden">';
2978 $out2 .= '<td colspan="2" class="no-padding">';
2979 $out2 .= $diff->Render($renderer);
2980 $out2 .= '</td>';
2981 $out2 .= '</tr>';
2982 $out2 .= '</table>';
2983 $out2 .= '</div>';
2984 } else {
2985 $out2 .= '<div class="wpr-table-container" data-table="' . $tbl_current['basename'] . '">';
2986 $out2 .= '<table>';
2987 $out2 .= '<tr title="Click to show/hide more info" class="wpr-table-difference header-row">';
2988 $out2 .= '<td><b>' . $tbl_current['fullname'] . '</b> data in tables does not match</td>';
2989 $out2 .= '<td><b>' . $tbl_snapshot['fullname'] . '</b> data in tables does not match<span class="dashicons dashicons-arrow-down-alt2"></span></td>';
2990 $out2 .= '</tr>';
2991 $out2 .= '<tr class="hidden">';
2992 $out2 .= '<td>';
2993 $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>';
2994 $out2 .= '</td>';
2995 $out2 .= '<td>';
2996 $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>';
2997 $out2 .= '</td>';
2998 $out2 .= '</tr>';
2999
3000 $out2 .= '<tr class="hidden">';
3001 $out2 .= '<td colspan="2">';
3002 if ($tbl_current['corename'] == 'options') {
3003 $ss_prefix = $tbl_snapshot['uid'] . '_' . $wpdb->prefix;
3004 //Due to the working with various table sets, not always prefixed with the WordPress Database Table prefix we need to use custom unprepared queries
3005 $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
3006 $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
3007 $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
3008 $out2 .= '<table class="table_diff">';
3009 $out2 .= '<tr><td style="width: 100px;"><b>Option Name</b></td><td><b>Current Value</b></td><td><b>Snapshot Value</b></td></tr>';
3010 foreach ($diff_rows as $row) {
3011 $out2 .= '<tr>';
3012 $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
3013 $out2 .= '<td>' . (empty($row->current_value) ? '<i>empty</i>' : $row->current_value) . '</td>';
3014 $out2 .= '<td>' . (empty($row->snapshot_value) ? '<i>empty</i>' : $row->snapshot_value) . '</td>';
3015 $out2 .= '</tr>';
3016 } // foreach
3017 foreach ($only_current as $row) {
3018 $out2 .= '<tr>';
3019 $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
3020 $out2 .= '<td>' . (empty($row->current_value) ? '<i>empty</i>' : $row->current_value) . '</td>';
3021 $out2 .= '<td><i>not found in snapshot</i></td>';
3022 $out2 .= '</tr>';
3023 } // foreach
3024 foreach ($only_current as $row) {
3025 $out2 .= '<tr>';
3026 $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
3027 $out2 .= '<td><i>not found in current tables</i></td>';
3028 $out2 .= '<td>' . (empty($row->snapshot_value) ? '<i>empty</i>' : $row->snapshot_value) . '</td>';
3029 $out2 .= '</tr>';
3030 } // foreach
3031 $out2 .= '</table>';
3032 } else {
3033 $out2 .= '<p class="textcenter">Detailed data diff is not available for this table.</p>';
3034 }
3035 $out2 .= '</td>';
3036 $out2 .= '</tr>';
3037
3038 $out2 .= '</table>';
3039 $out2 .= '</div>';
3040 }
3041 } // foreach in both
3042
3043 return $out . $out2 . $out3;
3044 } // do_compare_snapshots
3045
3046
3047 /**
3048 * Generates a unique snapshot ID; verified non-existing
3049 *
3050 * @return string
3051 */
3052 function generate_snapshot_uid($length = 6)
3053 {
3054 global $wpdb;
3055 $snapshots = $this->get_snapshots();
3056 $cnt = 0;
3057 $uid = false;
3058
3059 do {
3060 $cnt++;
3061 $uid = substr(str_shuffle(str_repeat('abcdefghijklmnopqrstuvwxyz', $length)), 0, $length);
3062 // phpcs:ignore db call warning as we are using uncommon queries
3063 $verify_db = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', array('%' . $uid . '%'))); // phpcs:ignore
3064 } while (!empty($verify_db) && isset($snapshots[$uid]) && $cnt < 30);
3065
3066 if ($cnt == 30) {
3067 $uid = false;
3068 }
3069
3070 return $uid;
3071 } // generate_snapshot_uid
3072
3073
3074 // auto download / install / activate WP Captcha plugin
3075 function install_wpcaptcha()
3076 {
3077 check_ajax_referer('install_wpcaptcha');
3078
3079 if (false === current_user_can('manage_options')) {
3080 wp_die('Sorry, you have to be an admin to run this action.');
3081 }
3082
3083 $plugin_slug = 'advanced-google-recaptcha/advanced-google-recaptcha.php';
3084 $plugin_zip = 'https://downloads.wordpress.org/plugin/advanced-google-recaptcha.latest-stable.zip';
3085
3086 @include_once ABSPATH . 'wp-admin/includes/plugin.php';
3087 @include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
3088 @include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
3089 @include_once ABSPATH . 'wp-admin/includes/file.php';
3090 @include_once ABSPATH . 'wp-admin/includes/misc.php';
3091 echo '<style>
3092 body{
3093 font-family: sans-serif;
3094 font-size: 14px;
3095 line-height: 1.5;
3096 color: #444;
3097 }
3098 </style>';
3099
3100 echo '<div style="margin: 20px; color:#444;">';
3101 echo 'If things are not done in a minute <a target="_parent" href="' . esc_url(admin_url('plugin-install.php?s=google%20recaptcha%20webfactory&tab=search&type=term')) . '">install the plugin manually via Plugins page</a><br><br>';
3102 echo 'Starting ...<br><br>';
3103
3104 wp_cache_flush();
3105 $upgrader = new Plugin_Upgrader();
3106 echo 'Check if Advanced Google ReCaptcha is already installed ... <br />';
3107 if ($this->is_plugin_installed($plugin_slug)) {
3108 echo 'Advanced Google ReCaptcha is already installed! <br /><br />Making sure it\'s the latest version.<br />';
3109 $upgrader->upgrade($plugin_slug);
3110 $installed = true;
3111 } else {
3112 echo 'Installing Advanced Google ReCaptcha.<br />';
3113 $installed = $upgrader->install($plugin_zip);
3114 }
3115 wp_cache_flush();
3116
3117 if (!is_wp_error($installed) && $installed) {
3118 echo 'Activating Advanced Google ReCaptcha.<br />';
3119 $activate = activate_plugin($plugin_slug);
3120
3121 if (is_null($activate)) {
3122 echo 'Advanced Google ReCaptcha Activated.<br />';
3123
3124 echo '<script>setTimeout(function() { top.location = "tools.php?page=wp-reset"; }, 1000);</script>';
3125 echo '<br>If you are not redirected in a few seconds - <a href="tools.php?page=wp-reset" target="_parent">click here</a>.';
3126 }
3127 } else {
3128 echo 'Could not install Advanced Google ReCaptcha. You\'ll have to <a target="_parent" href="' . esc_url(admin_url('plugin-install.php?s=google%20recaptcha%20webfactory&tab=search&type=term')) . '">download and install manually</a>.';
3129 }
3130
3131 echo '</div>';
3132 } // install_wpcaptcha
3133
3134 /**
3135 * Initializes the WordPress filesystem.
3136 *
3137 * @return bool
3138 */
3139 function wp_init_filesystem()
3140 {
3141 if (! $this->filesystem_initialized) {
3142 if (! class_exists('WP_Filesystem')) {
3143 require_once ABSPATH . 'wp-admin/includes/file.php';
3144 }
3145
3146 WP_Filesystem();
3147 $this->filesystem_initialized = true;
3148 }
3149
3150 return true;
3151 }
3152
3153 /**
3154 * Clean up on uninstall; no action on deactive at the moment
3155 *
3156 * @return null
3157 */
3158 static function uninstall()
3159 {
3160 delete_option('wp-reset');
3161 delete_option('wp-reset-snapshots');
3162 } // uninstall
3163
3164
3165 /**
3166 * Disabled; we use singleton pattern so magic functions need to be disabled
3167 *
3168 * @return null
3169 */
3170 function __clone() {}
3171
3172
3173 /**
3174 * Disabled; we use singleton pattern so magic functions need to be disabled
3175 *
3176 * @return null
3177 */
3178 function __sleep() {}
3179
3180
3181 /**
3182 * Disabled; we use singleton pattern so magic functions need to be disabled
3183 *
3184 * @return null
3185 */
3186 function __wakeup() {}
3187 } // WP_Reset class
3188
3189
3190 // Create plugin instance and hook things up
3191 // Only if in admin - plugin has no frontend functionality
3192 if (is_admin() || WP_Reset::is_cli_running()) {
3193 global $wp_reset;
3194 $wp_reset = WP_Reset::getInstance();
3195 add_action('plugins_loaded', array($wp_reset, 'load_textdomain'));
3196 register_uninstall_hook(__FILE__, array('WP_Reset', 'uninstall'));
3197 }
3198