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