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

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