PluginProbe
WP Reset / 2.01
WP Reset v2.01
trunk 1.0 1.1 1.11 1.20 1.25 1.30 1.35 1.40 1.45 1.50 1.55 1.60 1.65 1.70 1.75 1.77 1.80 1.81 1.82 1.83 1.84 1.85 1.86 1.90 All 42 releases
wp-reset / wp-reset.php

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

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