PluginProbe
WP Reset / 1.90
WP Reset v1.90
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 1.90, at wp-reset.php

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