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

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

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