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

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