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

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