WP Reset – Fastest WordPress Reset Plugin - Version 1.45

Version Description

Download this release

Release Info

Developer WebFactory
Plugin Icon 128x128 WP Reset – Fastest WordPress Reset Plugin
Version 1.45
Comparing to
See all releases

Code changes from version 1.40 to 1.45

Files changed (5) hide show
  1. css/wp-reset.css +11 -2
  2. js/wp-reset.js +31 -11
  3. readme.txt +16 -11
  4. wp-reset-cli.php +171 -19
  5. wp-reset.php +302 -136
css/wp-reset.css CHANGED
@@ -18,6 +18,11 @@
18
  text-align: center;
19
  }
20
 
 
 
 
 
 
21
  .tools_page_wp-reset.wp-core-ui .button,
22
  .tools_page_wp-reset.wp-core-ui .button-primary,
23
  .tools_page_wp-reset.wp-core-ui .button-secondary {
@@ -82,6 +87,10 @@
82
  line-height: 1.7;
83
  }
84
 
 
 
 
 
85
  .tools_page_wp-reset .rotating {
86
  -webkit-animation:spin 1.5s linear infinite;
87
  -moz-animation:spin 1.5s linear infinite;
@@ -245,8 +254,8 @@
245
  }
246
 
247
 
248
- @-moz-keyframes spin {
249
- 100% {
250
  -moz-transform: rotate(-360deg);
251
  }
252
  }
18
  text-align: center;
19
  }
20
 
21
+ .tools_page_wp-reset .button.disabled {
22
+ pointer-events: none;
23
+ cursor: not-allowed;
24
+ }
25
+
26
  .tools_page_wp-reset.wp-core-ui .button,
27
  .tools_page_wp-reset.wp-core-ui .button-primary,
28
  .tools_page_wp-reset.wp-core-ui .button-secondary {
87
  line-height: 1.7;
88
  }
89
 
90
+ .tools_page_wp-reset .button-delete:hover {
91
+ color: #cc2f2f;
92
+ }
93
+
94
  .tools_page_wp-reset .rotating {
95
  -webkit-animation:spin 1.5s linear infinite;
96
  -moz-animation:spin 1.5s linear infinite;
254
  }
255
 
256
 
257
+ @-moz-keyframes spin {
258
+ 100% {
259
  -moz-transform: rotate(-360deg);
260
  }
261
  }
js/wp-reset.js CHANGED
@@ -38,7 +38,7 @@ jQuery(document).ready(function($) {
38
  // delete themes
39
  $('.tools_page_wp-reset').on('click', '#delete-themes', 'click', function(e) {
40
  e.preventDefault();
41
-
42
  run_tool(this, 'delete_themes');
43
 
44
  return false;
@@ -48,19 +48,39 @@ jQuery(document).ready(function($) {
48
  // delete plugins
49
  $('.tools_page_wp-reset').on('click', '#delete-plugins', 'click', function(e) {
50
  e.preventDefault();
51
-
52
  run_tool(this, 'delete_plugins');
53
 
54
  return false;
55
  }); // delete plugins
56
 
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  // compare snapshot
59
  $('#wpr-snapshots').on('click', '.compare-snapshot', 'click', function(e) {
60
  e.preventDefault();
61
  uid = $(this).data('ss-uid');
62
  button = $(this);
63
-
64
  block_ui($(button).data('wait-msg'));
65
  $.get({
66
  url: ajaxurl,
@@ -84,7 +104,7 @@ jQuery(document).ready(function($) {
84
  focusConfirm: false,
85
  showCloseButton: true,
86
  customClass: 'compare-snapshots'
87
- });
88
  } else {
89
  swal({ type: 'error', title: wp_reset.documented_error + ' ' + data.data });
90
  }
@@ -100,7 +120,7 @@ jQuery(document).ready(function($) {
100
  $('#wpr-snapshots').on('click', '.restore-snapshot', 'click', function(e) {
101
  e.preventDefault();
102
  uid = $(this).data('ss-uid');
103
-
104
  run_tool(this, 'restore_snapshot', uid);
105
 
106
  return false;
@@ -143,7 +163,7 @@ jQuery(document).ready(function($) {
143
  $('#wpr-snapshots').on('click', '.delete-snapshot', 'click', function(e) {
144
  e.preventDefault();
145
  uid = $(this).data('ss-uid');
146
-
147
  run_tool(this, 'delete_snapshot', uid);
148
 
149
  return false;
@@ -154,7 +174,7 @@ jQuery(document).ready(function($) {
154
  $('.tools_page_wp-reset').on('click', '.create-new-snapshot', 'click', function(e) {
155
  e.preventDefault();
156
  button = $('#create-new-snapshot-primary');
157
-
158
  swal({ title: $(button).data('title'),
159
  type: 'question',
160
  text: $(button).data('text'),
@@ -165,7 +185,7 @@ jQuery(document).ready(function($) {
165
  confirmButtonText: $(button).data('btn-confirm'),
166
  cancelButtonText: wp_reset.cancel_button,
167
  width: 600
168
- }).then((result) => {
169
  if (typeof result.value != 'undefined') {
170
  block = block_ui($(button).data('msg-wait'));
171
  $.get({
@@ -306,7 +326,7 @@ jQuery(document).ready(function($) {
306
  type: 'error',
307
  confirmButtonText: wp_reset.ok_button,
308
  });
309
-
310
  e.preventDefault();
311
  return false;
312
  } // wrong confirmation code
@@ -358,7 +378,7 @@ jQuery(document).ready(function($) {
358
  return false;
359
  }); // toggle-card
360
 
361
-
362
  // init cards; collapse those that need collapsing
363
  cards = localStorage.getItem('wp-reset-cards');
364
  if (cards != null) {
@@ -370,7 +390,7 @@ jQuery(document).ready(function($) {
370
  }
371
  });
372
 
373
-
374
  // dismiss notice / pointer
375
  $('.wpr-dismiss-notice').on('click', function(e) {
376
  notice_name = $(this).data('notice');
38
  // delete themes
39
  $('.tools_page_wp-reset').on('click', '#delete-themes', 'click', function(e) {
40
  e.preventDefault();
41
+
42
  run_tool(this, 'delete_themes');
43
 
44
  return false;
48
  // delete plugins
49
  $('.tools_page_wp-reset').on('click', '#delete-plugins', 'click', function(e) {
50
  e.preventDefault();
51
+
52
  run_tool(this, 'delete_plugins');
53
 
54
  return false;
55
  }); // delete plugins
56
 
57
 
58
+ // drop custom tables
59
+ $('.tools_page_wp-reset').on('click', '#drop-custom-tables', 'click', function(e) {
60
+ e.preventDefault();
61
+
62
+ run_tool(this, 'drop_custom_tables');
63
+
64
+ return false;
65
+ }); // drop custom tables
66
+
67
+
68
+ // truncate custom tables
69
+ $('.tools_page_wp-reset').on('click', '#truncate-custom-tables', 'click', function(e) {
70
+ e.preventDefault();
71
+
72
+ run_tool(this, 'truncate_custom_tables');
73
+
74
+ return false;
75
+ }); // truncate custom tables
76
+
77
+
78
  // compare snapshot
79
  $('#wpr-snapshots').on('click', '.compare-snapshot', 'click', function(e) {
80
  e.preventDefault();
81
  uid = $(this).data('ss-uid');
82
  button = $(this);
83
+
84
  block_ui($(button).data('wait-msg'));
85
  $.get({
86
  url: ajaxurl,
104
  focusConfirm: false,
105
  showCloseButton: true,
106
  customClass: 'compare-snapshots'
107
+ });
108
  } else {
109
  swal({ type: 'error', title: wp_reset.documented_error + ' ' + data.data });
110
  }
120
  $('#wpr-snapshots').on('click', '.restore-snapshot', 'click', function(e) {
121
  e.preventDefault();
122
  uid = $(this).data('ss-uid');
123
+
124
  run_tool(this, 'restore_snapshot', uid);
125
 
126
  return false;
163
  $('#wpr-snapshots').on('click', '.delete-snapshot', 'click', function(e) {
164
  e.preventDefault();
165
  uid = $(this).data('ss-uid');
166
+
167
  run_tool(this, 'delete_snapshot', uid);
168
 
169
  return false;
174
  $('.tools_page_wp-reset').on('click', '.create-new-snapshot', 'click', function(e) {
175
  e.preventDefault();
176
  button = $('#create-new-snapshot-primary');
177
+
178
  swal({ title: $(button).data('title'),
179
  type: 'question',
180
  text: $(button).data('text'),
185
  confirmButtonText: $(button).data('btn-confirm'),
186
  cancelButtonText: wp_reset.cancel_button,
187
  width: 600
188
+ }).then((result) => {
189
  if (typeof result.value != 'undefined') {
190
  block = block_ui($(button).data('msg-wait'));
191
  $.get({
326
  type: 'error',
327
  confirmButtonText: wp_reset.ok_button,
328
  });
329
+
330
  e.preventDefault();
331
  return false;
332
  } // wrong confirmation code
378
  return false;
379
  }); // toggle-card
380
 
381
+
382
  // init cards; collapse those that need collapsing
383
  cards = localStorage.getItem('wp-reset-cards');
384
  if (cards != null) {
390
  }
391
  });
392
 
393
+
394
  // dismiss notice / pointer
395
  $('.wpr-dismiss-notice').on('click', function(e) {
396
  notice_name = $(this).data('notice');
readme.txt CHANGED
@@ -3,8 +3,8 @@ Tags: wordpress reset, reset wordpress, reset database, reset wordpress database
3
  Contributors: WebFactory, wpreset, googlemapswidget, securityninja, underconstructionpage
4
  Requires at least: 4.0
5
  Requires PHP: 5.2
6
- Tested up to: 4.9
7
- Stable tag: 1.40
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -56,7 +56,7 @@ Database snapshot is a copy of all WP database tables, standard and custom ones,
56
  Snapshots are primarily a development tool. Although they can be used for backups (and downloaded as gzipped SQL dumps), we suggest finding a more suitable tool for doing backups of live sites. Use snapshots to find out what changes a plugin made to your database - what custom tables were created, modified, deleted or what changes were made to site's settings. Or use it to quickly restore the development environment after testing database related changes.
57
  Restoring a snapshot does not affect other snapshots, or WP Reset settings. Snapshots can be compared to current database tables, restored (by overwriting current tables), exported ad gzipped SQL dumps, or deleted. Creating a snapshot on an average WordPress installation takes 1-2 seconds.
58
 
59
- https://youtu.be/xBfMmS12vMY
60
 
61
  #### Multisite (WP-MU) Support
62
 
@@ -64,14 +64,11 @@ WP Reset has yet to be completely tested with multisite! Please be careful when
64
 
65
  #### Partial Reset Tools
66
 
67
- Delete transients - deletes all transient related database entries. Including expired and non-expired transients, and orphaned timeout entries.
68
-
69
- Delete uploads - delete all files and folder in the /uploads/ folder.
70
-
71
- Delete plugins - deletes all plugins except WP Reset which remains active.
72
-
73
- Delete themes - deletes all themes.
74
-
75
 
76
 
77
  == Installation ==
@@ -98,6 +95,14 @@ Or if needed, upload manually;
98
  3. Additional tools for resetting and deleting various WordPress objects
99
 
100
  == Changelog ==
 
 
 
 
 
 
 
 
101
  = v1.40 =
102
  * 2018/10/24
103
  * new tool: DB Snapshots
3
  Contributors: WebFactory, wpreset, googlemapswidget, securityninja, underconstructionpage
4
  Requires at least: 4.0
5
  Requires PHP: 5.2
6
+ Tested up to: 5.0
7
+ Stable tag: 1.45
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
56
  Snapshots are primarily a development tool. Although they can be used for backups (and downloaded as gzipped SQL dumps), we suggest finding a more suitable tool for doing backups of live sites. Use snapshots to find out what changes a plugin made to your database - what custom tables were created, modified, deleted or what changes were made to site's settings. Or use it to quickly restore the development environment after testing database related changes.
57
  Restoring a snapshot does not affect other snapshots, or WP Reset settings. Snapshots can be compared to current database tables, restored (by overwriting current tables), exported ad gzipped SQL dumps, or deleted. Creating a snapshot on an average WordPress installation takes 1-2 seconds.
58
 
59
+ https://youtu.be/xBfMmS12vMY?rel=0
60
 
61
  #### Multisite (WP-MU) Support
62
 
64
 
65
  #### Partial Reset Tools
66
 
67
+ * Delete transients - deletes all transient related database entries. Including expired and non-expired transients, and orphaned timeout entries.
68
+ * Delete uploads - delete all files and folder in the /uploads/ folder.
69
+ * Delete plugins - deletes all plugins except WP Reset which remains active.
70
+ * Delete themes - deletes all themes.
71
+ * Empty or delete custom tables - empties (truncates) or deletes (drops) all custom database tables.
 
 
 
72
 
73
 
74
  == Installation ==
95
  3. Additional tools for resetting and deleting various WordPress objects
96
 
97
  == Changelog ==
98
+
99
+ = v1.45 =
100
+ * 2018/11/27
101
+ * new tool: truncate or drop custom DB tables
102
+ * truncate / drop tables tool added to WP-CLI
103
+ * all snapshot tools added to WP-CLI
104
+ * 80k users hit on 2018/11/15 with 430,800 downloads; 30 days for +10k & 57k downloads
105
+
106
  = v1.40 =
107
  * 2018/10/24
108
  * new tool: DB Snapshots
wp-reset-cli.php CHANGED
@@ -21,16 +21,16 @@ class WP_Reset_CLI extends WP_CLI_Command {
21
  * Reset the site database to default values. No files are modified.
22
  *
23
  * ## OPTIONS
24
- *
25
  * [--reactivate-theme]
26
  * : Reactivate currently active theme after reset.
27
- *
28
  * [--reactivate-plugins]
29
  * : Reactivate all currently active plugins after reset.
30
- *
31
  * [--deactivate-wp-reset]
32
  * : Deactivate WP Reset plugin after reset. By default it will stay active after reset.
33
- *
34
  * [--yes]
35
  * : Answer yes to the confirmation message.
36
  *
@@ -43,7 +43,7 @@ class WP_Reset_CLI extends WP_CLI_Command {
43
  */
44
  function reset( $args, $assoc_args ) {
45
  WP_CLI::confirm( 'Are you sure you want to reset the site? There is NO UNDO!', $assoc_args );
46
-
47
  global $wp_reset;
48
  $params = array();
49
 
@@ -70,42 +70,48 @@ class WP_Reset_CLI extends WP_CLI_Command {
70
 
71
  /**
72
  * Display WP Reset version.
73
- *
74
  * @when after_wp_load
75
  */
76
  function version( $args, $assoc_args ) {
77
  global $wp_reset;
78
-
79
  WP_CLI::line( 'WP Reset v' . $wp_reset->version );
80
  } // version
81
 
82
 
83
  /**
84
  * Delete selected WordPress objects.
85
- *
86
  * ## OPTIONS
87
- *
88
- * <plugins|themes|transients|uploads>
89
  * : WP objects to delete.
90
- *
91
  * [--yes]
92
  * : Answer yes to the confirmation message.
93
- *
 
 
 
94
  * ## EXAMPLES
95
  *
96
  * $ wp reset delete themes --yes
97
  * Success: 3 themes have been deleted.
98
- *
 
 
 
99
  * @when after_wp_load
100
  */
101
  function delete( $args, $assoc_args ) {
102
- global $wp_reset;
103
-
104
  if ( empty( $args[0] ) ) {
105
- WP_CLI::error( 'Please choose a subcommand: plugins, themes, transients or uploads.' );
106
  return;
107
- } elseif ( false == in_array( $args[0], array( 'themes', 'plugins', 'transients', 'uploads' ) ) ) {
108
- WP_CLI::error( 'Unknown subcommand. Please choose from: plugins, themes, transients or uploads.' );
109
  } else {
110
  $subcommand = $args[0];
111
  }
@@ -131,12 +137,158 @@ class WP_Reset_CLI extends WP_CLI_Command {
131
  $cnt = $wp_reset->do_delete_uploads();
132
  WP_CLI::success( $cnt . ' files & folders have been deleted.' );
133
  break;
 
 
 
 
 
 
 
 
 
 
 
134
  default:
135
  // should never come to this but can't hurt
136
- WP_CLI::error( 'Unknown subcommand. Please choose from: plugins, themes, transients or uploads.' );
137
  return;
138
  }
139
  } // delete
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  } // WP_Reset_CLI
141
 
142
  WP_CLI::add_command( 'reset', 'WP_Reset_CLI' );
21
  * Reset the site database to default values. No files are modified.
22
  *
23
  * ## OPTIONS
24
+ *
25
  * [--reactivate-theme]
26
  * : Reactivate currently active theme after reset.
27
+ *
28
  * [--reactivate-plugins]
29
  * : Reactivate all currently active plugins after reset.
30
+ *
31
  * [--deactivate-wp-reset]
32
  * : Deactivate WP Reset plugin after reset. By default it will stay active after reset.
33
+ *
34
  * [--yes]
35
  * : Answer yes to the confirmation message.
36
  *
43
  */
44
  function reset( $args, $assoc_args ) {
45
  WP_CLI::confirm( 'Are you sure you want to reset the site? There is NO UNDO!', $assoc_args );
46
+
47
  global $wp_reset;
48
  $params = array();
49
 
70
 
71
  /**
72
  * Display WP Reset version.
73
+ *
74
  * @when after_wp_load
75
  */
76
  function version( $args, $assoc_args ) {
77
  global $wp_reset;
78
+
79
  WP_CLI::line( 'WP Reset v' . $wp_reset->version );
80
  } // version
81
 
82
 
83
  /**
84
  * Delete selected WordPress objects.
85
+ *
86
  * ## OPTIONS
87
+ *
88
+ * <plugins|themes|transients|uploads|custom-tables>
89
  * : WP objects to delete.
90
+ *
91
  * [--yes]
92
  * : Answer yes to the confirmation message.
93
+ *
94
+ * [--empty]
95
+ * : Empty (truncate) custom tables instead of deleting (dropping) them.
96
+ *
97
  * ## EXAMPLES
98
  *
99
  * $ wp reset delete themes --yes
100
  * Success: 3 themes have been deleted.
101
+ *
102
+ * $ wp reset delete custom-tables --truncate --yes
103
+ * Success: 3 custom tables have been emptied.
104
+ *
105
  * @when after_wp_load
106
  */
107
  function delete( $args, $assoc_args ) {
108
+ global $wp_reset, $wpdb;
109
+
110
  if ( empty( $args[0] ) ) {
111
+ WP_CLI::error( 'Please choose a subcommand: plugins, themes, transients, uploads or custom-tables.' );
112
  return;
113
+ } elseif ( false == in_array( $args[0], array( 'themes', 'plugins', 'transients', 'uploads', 'custom-tables' ) ) ) {
114
+ WP_CLI::error( 'Unknown subcommand. Please choose from: plugins, themes, transients, uploads or custom tables.' );
115
  } else {
116
  $subcommand = $args[0];
117
  }
137
  $cnt = $wp_reset->do_delete_uploads();
138
  WP_CLI::success( $cnt . ' files & folders have been deleted.' );
139
  break;
140
+ case 'custom-tables':
141
+ if ( !empty( $assoc_args['empty'] ) ) {
142
+ WP_CLI::confirm( 'Are you sure you want to empty (truncate) all custom tables (prefix: ' . $wpdb->prefix . ')?', $assoc_args );
143
+ $cnt = $wp_reset->do_truncate_custom_tables();
144
+ WP_CLI::success( $cnt . ' custom tables have been emptied.' );
145
+ } else {
146
+ WP_CLI::confirm( 'Are you sure you want to delete (drop) all custom tables (prefix: ' . $wpdb->prefix . ')?', $assoc_args );
147
+ $cnt = $wp_reset->do_drop_custom_tables();
148
+ WP_CLI::success( $cnt . ' custom tables have been deleted.' );
149
+ }
150
+ break;
151
  default:
152
  // should never come to this but can't hurt
153
+ WP_CLI::error( 'Unknown subcommand. Please choose from: plugins, themes, transients, uploads or custom-tables.' );
154
  return;
155
  }
156
  } // delete
157
+
158
+
159
+ /**
160
+ * List and manipulate DB snapshots.
161
+ *
162
+ * ## OPTIONS
163
+ *
164
+ * <list|create|restore|export|delete>
165
+ * : Action to perform with snapshot.
166
+ *
167
+ * [--yes]
168
+ * : Answer yes to the confirmation message.
169
+ *
170
+ * [--id=<snapshot-id>]
171
+ * : Specify snapshot ID when doing restore, export and delete.
172
+ *
173
+ * [--name=<snapshot-name>]
174
+ * : When creating a new snapshot specify an optional name.
175
+ *
176
+ * ## EXAMPLES
177
+ *
178
+ * wp reset snapshots create --yes
179
+ * Success: New snapshot with ID 089bea has been created.
180
+ *
181
+ * $ wp reset snapshots delete --id=123456
182
+ * Success: Snapshot has been deleted.
183
+ *
184
+ * $ wp reset snapshots export --id=123456
185
+ * Success: Snapshot has been exported and saved to: https://test.site/wp-content/wp-reset-snapshots-export/wp-reset-snapshot-123456.sql.gz
186
+ *
187
+ * @when after_wp_load
188
+ */
189
+ function snapshots( $args, $assoc_args ) {
190
+ global $wp_reset;
191
+
192
+ if ( empty( $args[0] ) ) {
193
+ WP_CLI::error( 'Please choose a subcommand: list, create, restore, export or delete.' );
194
+ return;
195
+ } elseif ( false == in_array( $args[0], array( 'list', 'create', 'restore', 'export', 'delete' ) ) ) {
196
+ WP_CLI::error( 'Unknown subcommand. Please choose from: list, create, restore, export or delete.' );
197
+ } else {
198
+ $subcommand = $args[0];
199
+ }
200
+
201
+ switch ($subcommand) {
202
+ case 'list':
203
+ if ( $snapshots = $wp_reset->get_snapshots() ) {
204
+ $table = array();
205
+ foreach ( $snapshots as $ss ) {
206
+ $tmp = array();
207
+ $tmp['id'] = $ss['uid'];
208
+ if ( !empty( $ss['name'] ) ) {
209
+ $tmp['name'] = $ss['name'];
210
+ } else {
211
+ $tmp['name'] = 'n/a';
212
+ }
213
+ $tmp['created'] = date( get_option( 'date_format' ), strtotime( $ss['timestamp'] ) ) . ' @ ' . date( get_option( 'time_format' ), strtotime( $ss['timestamp'] ) );
214
+ $tmp['info'] = $ss['tbl_core'] . ' standard & ';
215
+ if ( $ss['tbl_custom'] ) {
216
+ $tmp['info'] .= $ss['tbl_custom'] . ' custom table' . ( $ss['tbl_custom'] == 1? '': 's' );
217
+ } else {
218
+ $tmp['info'] .= 'no custom tables';
219
+ }
220
+ $tmp['info'] .= ' totaling ' . $wp_reset->format_size( $ss['tbl_size'] ) . ' in ' . number_format( $ss['tbl_rows'] ) . ' rows';
221
+
222
+ $table[] = $tmp;
223
+ } // foreach
224
+ WP_CLI\Utils\format_items( 'table', $table, array('id', 'name', 'created', 'info') );
225
+ } else {
226
+ WP_CLI::line( 'There are no saved snapshots.' );
227
+ }
228
+ break;
229
+ case 'create':
230
+ if ( !empty( $assoc_args['name'] ) ) {
231
+ $name = trim( $assoc_args['name'] );
232
+ } else {
233
+ $name = '';
234
+ }
235
+
236
+ WP_CLI::confirm( 'Are you sure you want to create a new snapshot?', $assoc_args );
237
+ $new = $wp_reset->do_create_snapshot( $name );
238
+ if ( is_wp_error( $new ) ) {
239
+ WP_CLI::error( $new->get_error_message() );
240
+ } else {
241
+ WP_CLI::success( 'New snapshot with ID ' . $new['uid'] . ' has been created.' );
242
+ }
243
+ break;
244
+ case 'restore':
245
+ if ( empty( $assoc_args['id'] ) ) {
246
+ WP_CLI::error( 'Please specify the snapshot ID with the "--id=123456" param. Use "wp reset snapshots list" to get a list of all snapshots.' );
247
+ break;
248
+ } else {
249
+ WP_CLI::confirm( 'Are you sure you want to restore the site to the snapshot with ID ' . $assoc_args['id'] . '?', $assoc_args );
250
+ $restore = $wp_reset->do_restore_snapshot( $assoc_args['id'] );
251
+ if ( is_wp_error( $restore ) ) {
252
+ WP_CLI::error( $restore->get_error_message() );
253
+ } else {
254
+ WP_CLI::success( 'Site has been restored to the selected snapshot.' );
255
+ }
256
+ }
257
+ break;
258
+ case 'export':
259
+ if ( empty( $assoc_args['id'] ) ) {
260
+ WP_CLI::error( 'Please specify the snapshot ID with the "--id=123456" param. Use "wp reset snapshots list" to get a list of all snapshots.' );
261
+ break;
262
+ } else {
263
+ $export = $wp_reset->do_export_snapshot( $assoc_args['id'] );
264
+ if ( is_wp_error( $export ) ) {
265
+ WP_CLI::error( $export->get_error_message() );
266
+ } else {
267
+ $url = content_url() . '/' . $wp_reset->snapshots_folder . '/' . $export;
268
+ WP_CLI::success( 'Snapshot has been exported and saved to: ' . $url );
269
+ }
270
+ }
271
+ break;
272
+ case 'delete':
273
+ if ( empty( $assoc_args['id'] ) ) {
274
+ WP_CLI::error( 'Please specify the snapshot ID with the "--id=123456" param. Use "wp reset snapshots list" to get a list of all snapshots.' );
275
+ break;
276
+ } else {
277
+ WP_CLI::confirm( 'Are you sure you want to delete the snapshot with ID ' . $assoc_args['id'] . '?', $assoc_args );
278
+ $del = $wp_reset->do_delete_snapshot( $assoc_args['id'] );
279
+ if ( is_wp_error( $del ) ) {
280
+ WP_CLI::error( $del->get_error_message() );
281
+ } else {
282
+ WP_CLI::success( 'Snapshot has been deleted.' );
283
+ }
284
+ }
285
+ break;
286
+ default:
287
+ // it should never come to this but can't hurt
288
+ WP_CLI::error( 'Unknown subcommand. Please choose from: list, create, restore, export or delete.' );
289
+ return;
290
+ }
291
+ } // snapshots
292
  } // WP_Reset_CLI
293
 
294
  WP_CLI::add_command( 'reset', 'WP_Reset_CLI' );
wp-reset.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP Reset
4
  Plugin URI: https://wpreset.com/
5
  Description: Reset the site to default installation values without modifying any files. Deletes all customizations and content.
6
- Version: 1.40
7
  Author: WebFactory Ltd
8
  Author URI: https://www.webfactoryltd.com/
9
  Text Domain: wp-reset
@@ -49,7 +49,7 @@ class WP_Reset {
49
 
50
  /**
51
  * Creates a new WP_Reset object and implements singleton
52
- *
53
  * @return WP_Reset
54
  */
55
  static function getInstance() {
@@ -63,7 +63,7 @@ class WP_Reset {
63
 
64
  /**
65
  * Initialize properties, hook to filters and actions
66
- *
67
  * @return null
68
  */
69
  private function __construct() {
@@ -71,24 +71,25 @@ class WP_Reset {
71
  $this->plugin_dir = plugin_dir_path(__FILE__);
72
  $this->plugin_url = plugin_dir_url(__FILE__);
73
  $this->load_options();
74
-
75
  add_action('admin_menu', array($this, 'admin_menu'));
76
  add_action('admin_init', array($this, 'do_all_actions'));
77
  add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
78
  add_action('wp_ajax_wp_reset_dismiss_notice', array($this, 'ajax_dismiss_notice'));
79
  add_action('wp_ajax_wp_reset_run_tool', array($this, 'ajax_run_tool'));
80
-
81
  add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'));
82
  add_filter('plugin_row_meta', array($this, 'plugin_meta_links'), 10, 2);
83
  add_filter('admin_footer_text', array($this, 'admin_footer_text'));
 
84
 
85
  $this->core_tables = array_map(function($tbl) { global $wpdb; return $wpdb->prefix . $tbl; }, $this->core_tables);
86
  } // __construct
87
 
88
-
89
  /**
90
  * Get plugin version from file header
91
- *
92
  * @return string
93
  */
94
  function get_plugin_version() {
@@ -96,12 +97,12 @@ class WP_Reset {
96
 
97
  return $plugin_data['version'];
98
  } // get_plugin_version
99
-
100
 
101
  /**
102
  * Load and prepare the options array
103
  * If needed create a new DB entry
104
- *
105
  * @return array
106
  */
107
  private function load_options() {
@@ -127,7 +128,7 @@ class WP_Reset {
127
  if ($change) {
128
  update_option('wp-reset', $options, true);
129
  }
130
-
131
  $this->options = $options;
132
  return $options;
133
  } // load_options
@@ -135,7 +136,7 @@ class WP_Reset {
135
 
136
  /**
137
  * Get meta part of plugin options
138
- *
139
  * @return array
140
  */
141
  function get_meta() {
@@ -145,14 +146,14 @@ class WP_Reset {
145
 
146
  /**
147
  * Get all dismissed notices, or check for one specific notice
148
- *
149
  * @param string $notice_name Optional. Check if specified notice is dismissed.
150
- *
151
  * @return bool|array
152
  */
153
  function get_dismissed_notices($notice_name = '') {
154
  $notices = $this->options['dismissed_notices'];
155
-
156
  if (empty($notice_name)) {
157
  return $notices;
158
  } else {
@@ -167,11 +168,11 @@ class WP_Reset {
167
 
168
  /**
169
  * Get options part of plugin options
170
- *
171
  * todo: not completed
172
- *
173
  * @param string $key Optional.
174
- *
175
  * @return array
176
  */
177
  function get_options($key = '') {
@@ -181,12 +182,12 @@ class WP_Reset {
181
 
182
  /**
183
  * Update plugin options, currently entire array
184
- *
185
  * todo: this handles the entire options array although it should only do the options part - it's confusing
186
- *
187
  * @param string $key Data to save.
188
- * @param string $data Option key.
189
- *
190
  * @return bool
191
  */
192
  function update_options($key, $data) {
@@ -196,10 +197,10 @@ class WP_Reset {
196
  return $tmp;
197
  } // set_options
198
 
199
-
200
  /**
201
  * Add plugin menu entry under Tools menu
202
- *
203
  * @return null
204
  */
205
  function admin_menu() {
@@ -209,7 +210,7 @@ class WP_Reset {
209
 
210
  /**
211
  * Dismiss notice via AJAX call
212
- *
213
  * @return null
214
  */
215
  function ajax_dismiss_notice() {
@@ -226,9 +227,9 @@ class WP_Reset {
226
 
227
  /**
228
  * Dismiss notice by adding it to dismissed_notices options array
229
- *
230
  * @param string $notice_name Notice to dismiss.
231
- *
232
  * @return bool
233
  */
234
  function dismiss_notice($notice_name) {
@@ -245,7 +246,7 @@ class WP_Reset {
245
 
246
  /**
247
  * Returns all WP pointers
248
- *
249
  * @return array
250
  */
251
  function get_pointers() {
@@ -259,7 +260,7 @@ class WP_Reset {
259
 
260
  /**
261
  * Enqueue CSS and JS files
262
- *
263
  * @return null
264
  */
265
  function admin_enqueue_scripts($hook) {
@@ -287,7 +288,7 @@ class WP_Reset {
287
  if ('tools_page_wp-reset' != $hook) {
288
  return;
289
  }
290
-
291
  $options = $this->get_options();
292
 
293
  $js_localize = array('undocumented_error' => __('An undocumented error has occured. Please refresh the page and try again.', 'wp-reset'),
@@ -330,11 +331,11 @@ class WP_Reset {
330
  wp_dequeue_style('wpmegmaps-jqueryui');
331
  wp_dequeue_style('wp-botwatch-css');
332
  } // admin_enqueue_scripts
333
-
334
 
335
  /**
336
  * Check if WP-CLI is available and running
337
- *
338
  * @return bool
339
  */
340
  function is_cli_running() {
@@ -348,38 +349,38 @@ class WP_Reset {
348
 
349
  /**
350
  * Deletes all transients.
351
- *
352
  * @return int Number of deleted transient DB entries
353
  */
354
  function do_delete_transients() {
355
  global $wpdb;
356
-
357
  $count = $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '\_transient\_%' OR option_name LIKE '\_site\_transient\_%'");
358
-
359
  return $count;
360
  } // do_delete_transients
361
-
362
 
363
  /**
364
  * Deletes all files in uploads folder.
365
- *
366
  * @return int Number of deleted files and folders.
367
  */
368
  function do_delete_uploads() {
369
  $upload_dir = wp_get_upload_dir();
370
 
371
  $this->delete_folder($upload_dir['basedir'], $upload_dir['basedir']);
372
-
373
  return $this->delete_count;
374
  } // do_delete_uploads
375
 
376
 
377
  /**
378
  * Recursively deletes a folder
379
- *
380
  * @param string $folder Recursive param.
381
  * @param string $base_folder Base folder.
382
- *
383
  * @return bool
384
  */
385
  private function delete_folder($folder, $base_folder) {
@@ -407,9 +408,9 @@ class WP_Reset {
407
  /**
408
  * Deactivate and delete all plugins
409
  *
410
- * @param bool $keep_wp_reset Keep WP Reset active and installed
411
  * @param bool $silent_deactivate Skip individual plugin deactivation functions when deactivating
412
- *
413
  * @return int Number of deleted plugins.
414
  */
415
  function do_delete_plugins($keep_wp_reset = true, $silent_deactivate = false) {
@@ -444,7 +445,7 @@ class WP_Reset {
444
  * Delete all themes
445
  *
446
  * @param bool $keep_default_theme Keep default theme
447
- *
448
  * @return int Number of deleted themes.
449
  */
450
  function do_delete_themes($keep_default_theme = true) {
@@ -458,7 +459,7 @@ class WP_Reset {
458
  foreach ($all_themes as $theme_slug => $theme_details) {
459
  $res = delete_theme($theme_slug);
460
  }
461
-
462
  if (false == $keep_default_theme) {
463
  update_option('template', '');
464
  update_option('stylesheet', '');
@@ -468,9 +469,43 @@ class WP_Reset {
468
  } // do_delete_themes
469
 
470
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
  /**
472
  * Run one tool via AJAX call
473
- *
474
  * @return null
475
  */
476
  function ajax_run_tool() {
@@ -478,7 +513,7 @@ class WP_Reset {
478
 
479
  $tool = trim(@$_GET['tool']);
480
  $extra_data = trim(@$_GET['extra_data']);
481
-
482
  if ($tool == 'delete_transients') {
483
  $cnt = $this->do_delete_transients();
484
  wp_send_json_success($cnt);
@@ -491,6 +526,12 @@ class WP_Reset {
491
  } elseif ($tool == 'delete_uploads') {
492
  $cnt = $this->do_delete_uploads();
493
  wp_send_json_success($cnt);
 
 
 
 
 
 
494
  } elseif ($tool == 'delete_snapshot') {
495
  $res = $this->do_delete_snapshot($extra_data);
496
  if (is_wp_error($res)) {
@@ -537,9 +578,9 @@ class WP_Reset {
537
  * Reinstall / reset the WP site
538
  * There are no failsafes in the function - it reinstalls when called
539
  * Redirects when done
540
- *
541
  * @param array $params Optional.
542
- *
543
  * @return null
544
  */
545
  function do_reinstall($params = array()) {
@@ -563,7 +604,7 @@ class WP_Reset {
563
  $siteurl = get_option('siteurl');
564
  $home = get_option('home');
565
  $snapshots = $this->get_snapshots();
566
-
567
  $active_plugins = get_option('active_plugins');
568
  $active_theme = wp_get_theme();
569
 
@@ -624,7 +665,7 @@ class WP_Reset {
624
  if (!empty($params['reactivate_plugins'])) {
625
  foreach ($active_plugins as $plugin_file) {
626
  activate_plugin($plugin_file);
627
- }
628
  }
629
 
630
  if (!$this->is_cli_running()) {
@@ -637,12 +678,12 @@ class WP_Reset {
637
  exit;
638
  }
639
  } // do_reinstall
640
-
641
 
642
  /**
643
  * Checks wp_reset post value and performs all actions
644
  * todo: handle messages for various actions
645
- *
646
  * @return null|bool
647
  */
648
  function do_all_actions() {
@@ -660,7 +701,7 @@ class WP_Reset {
660
  add_settings_error('wp-reset', 'bad-nonce', __('Something went wrong. Please refresh the page and try again.', 'wp-reset'), 'error');
661
  return false;
662
  }
663
-
664
  // check confirmation code
665
  if (true === isset($_POST['wp_reset_confirm']) && 'reset' !== $_POST['wp_reset_confirm']) {
666
  add_settings_error('wp-reset', 'bad-confirm', __('<b>Invalid confirmation code.</b> Please type "reset" in the confirmation field.', 'wp-reset'), 'error');
@@ -680,49 +721,49 @@ class WP_Reset {
680
 
681
 
682
  /**
683
- * Add "Reset WordPress" action link to plugins table, left part
684
- *
685
  * @param array $links Initial list of links.
686
- *
687
  * @return array
688
  */
689
  function plugin_action_links($links) {
690
- $settings_link = '<a href="' . admin_url('tools.php?page=wp-reset') . '" title="' . __('Reset WordPress', 'wp-reset') . '">' . __('Reset WordPress', 'wp-reset') . '</a>';
691
 
692
  array_unshift($links, $settings_link);
693
 
694
  return $links;
695
  } // plugin_action_links
696
-
697
-
698
  /**
699
  * Add links to plugin's description in plugins table
700
- *
701
  * @param array $links Initial list of links.
702
  * @param string $file Basename of current plugin.
703
- *
704
  * @return array
705
  */
706
  function plugin_meta_links($links, $file) {
707
  if ($file !== plugin_basename(__FILE__)) {
708
  return $links;
709
  }
710
-
711
  $support_link = '<a target="_blank" href="https://wordpress.org/support/plugin/wp-reset" title="' . __('Get help', 'wp-reset') . '">' . __('Support', 'wp-reset') . '</a>';
712
  $home_link = '<a target="_blank" href="' . $this->generate_web_link('plugins-table-right') . '" title="' . __('Plugin Homepage', 'wp-reset') . '">' . __('Plugin Homepage', 'wp-reset') . '</a>';
713
  $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>';
714
-
715
  $links[] = $support_link;
716
  $links[] = $home_link;
717
  $links[] = $rate_link;
718
-
719
  return $links;
720
  } // plugin_meta_links
721
 
722
 
723
  /**
724
  * Test if we're on WPR's admin page
725
- *
726
  * @return bool
727
  */
728
  function is_plugin_page() {
@@ -738,9 +779,9 @@ class WP_Reset {
738
 
739
  /**
740
  * Add powered by text in admin footer
741
- *
742
  * @param string $text Default footer text.
743
- *
744
  * @return string
745
  */
746
  function admin_footer_text($text) {
@@ -756,7 +797,7 @@ class WP_Reset {
756
 
757
  /**
758
  * Loads plugin's translated strings
759
- *
760
  * @return null
761
  */
762
  function load_textdomain() {
@@ -766,7 +807,7 @@ class WP_Reset {
766
 
767
  /**
768
  * Inform the user that WordPress has been successfully reset
769
- *
770
  * @return null
771
  */
772
  function notice_successfull_reset() {
@@ -778,7 +819,7 @@ class WP_Reset {
778
 
779
  /**
780
  * Outputs complete plugin's admin page
781
- *
782
  * @return null
783
  */
784
  function plugin_page() {
@@ -804,7 +845,7 @@ class WP_Reset {
804
  echo '</div>';
805
  $notice_shown = true;
806
  }
807
-
808
  if ((!empty($meta['reset_count']) || !empty($snapshots)) && false === $notice_shown && false == $this->get_dismissed_notices('rate')) {
809
  echo '<div class="card notice-wrapper">';
810
  echo '<h2>' . __('Please help us keep the plugin free &amp; up-to-date', 'wp-reset') . '</h2>';
@@ -813,7 +854,7 @@ class WP_Reset {
813
  echo '</div>';
814
  $notice_shown = true;
815
  }
816
-
817
  // Tidy Repo ad
818
  // disabled for now
819
  if (false && false === $notice_shown && $meta['reset_count'] >= 2 && false == $this->get_dismissed_notices('tidy')) {
@@ -845,7 +886,7 @@ class WP_Reset {
845
  echo '<div style="display: none;" id="tab-tools">';
846
  $this->tab_tools();
847
  echo '</div>';
848
-
849
  echo '<div style="display: none;" id="tab-snapshots">';
850
  $this->tab_snapshots();
851
  echo '</div>';
@@ -853,7 +894,7 @@ class WP_Reset {
853
  echo '<div style="display: none;" id="tab-support">';
854
  $this->tab_support();
855
  echo '</div>';
856
-
857
  if (empty($notices['geoip_tab'])) {
858
  echo '<div style="display: none;" id="tab-geoip">';
859
  $this->tab_geoip();
@@ -865,11 +906,11 @@ class WP_Reset {
865
  echo '</form>';
866
  echo '</div>'; // wrap
867
  } // plugin_page
868
-
869
 
870
  /**
871
  * Echoes content for reset tab
872
- *
873
  * @return null
874
  */
875
  private function tab_reset() {
@@ -920,7 +961,7 @@ class WP_Reset {
920
  echo '<div class="card">';
921
  echo '<h2>' . __('Reset', 'wp-reset') . '</h2>';
922
  echo '<p>' . __('Type <b>reset</b> in the confirmation field to confirm the reset and then click the "Reset WordPress" button. <b>There is NO UNDO. No backups are made by WP Reset.</b>', 'wp-reset') . '</p>';
923
-
924
  wp_nonce_field('wp-reset');
925
  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;';
926
  echo '<input id="wp_reset_submit" type="button" class="button-primary" value="' . __('Reset WordPress', 'wp-reset') . '"></p>';
@@ -930,48 +971,74 @@ class WP_Reset {
930
 
931
  /**
932
  * Echoes content for tools tab
933
- *
934
  * @return null
935
  */
936
  private function tab_tools() {
937
- $theme = wp_get_theme();
938
 
939
  echo '<div class="card">';
940
- echo '<h2>' . __('Transients', 'wp-reset') . '</h2>';
941
- echo '<p>' . __('All transient related database entries will be deleted. Including expired and non-expired transients, and orphaned timeout entries. <b>There is NO UNDO. WP Reset will not make any backups.</b>', 'wp-reset') . '</p>';
942
- echo '<p><a 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. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n transient database entries have been deleted." class="button" href="#" id="delete-transients">Delete all transients</a></p>';
943
  echo '</div>';
944
 
945
  $upload_dir = wp_upload_dir(date('Y/m'), true);
 
946
 
947
  echo '<div class="card">';
948
- echo '<h2>' . __('Uploads Folder', 'wp-reset') . '</h2>';
949
- echo '<p>' . __('All files in <code>' . $upload_dir['basedir'] . '</code> folder will be deleted. Including folders and subfolder, and files in subfolders. Files associated with <a href="' . admin_url('upload.php') . '">media</a> entries will be deleted too. <b>There is NO UNDO. WP Reset will not make any backups.</b>', 'wp-reset') . '</p>';
950
  if (false != $upload_dir['error']) {
951
  echo '<p><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>';
952
  } else {
953
- echo '<p><a 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 will not make any backups." data-text-done="%n files &amp; folders have been deleted." class="button" href="#" id="delete-uploads">Delete all files &amp; folders in uploads folder</a></p>';
954
  }
955
  echo '</div>';
956
 
 
 
 
 
 
 
 
 
957
  echo '<div class="card">';
958
- echo '<h2>' . __('Themes', 'wp-reset') . '</h2>';
959
- echo '<p>' . __('All themes will be deleted. Including the currently active theme - ' . $theme->get('Name') . '. <b>There is NO UNDO. WP Reset will not make any backups.</b>', 'wp-reset') . '</p>';
960
- echo '<p><a 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 will not make any backups." data-text-done="%n themes have been deleted." class="button" href="#" id="delete-themes">Delete all themes</a></p>';
961
  echo '</div>';
962
 
 
 
963
  echo '<div class="card">';
964
- echo '<h2>' . __('Plugins', 'wp-reset') . '</h2>';
965
- echo '<p>' . __('Type <b>reset</b> in the confirmation field to confirm the reset and then click the "Reset WordPress" button. <b>There is NO UNDO. WP Reset will not make any backups.</b>', 'wp-reset') . '</p>';
966
- echo '<p>WP Reset plugin will no be deleted or disabled.</p>';
967
- echo '<p><a 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 will not make any backups." data-text-done="%n plugins have been deleted." class="button" href="#" id="delete-plugins">Delete plugins</a></p>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
968
  echo '</div>';
969
  } // tab_tools
970
 
971
 
972
  /**
973
  * Echoes content for support tab
974
- *
975
  * @return null
976
  */
977
  private function tab_support() {
@@ -990,11 +1057,11 @@ class WP_Reset {
990
  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. Thank you!', 'wp-reset') . '</p>';
991
  echo '</div>';
992
  } // tab_support
993
-
994
-
995
  /**
996
  * Echoes content for snapshots tab
997
- *
998
  * @return null
999
  */
1000
  private function tab_snapshots() {
@@ -1007,7 +1074,7 @@ class WP_Reset {
1007
  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.<br>
1008
  Snapshots are primarily a development tool. Although they can be used for backups (and downloaded), we suggest finding a more suitable tool for live sites, such as <a href="https://wordpress.org/plugins/updraftplus/" target="_blank">UpdraftPlus</a>. 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.<br>Restoring a snapshot does not affect other snapshots, or WP Reset settings.</p>';
1009
  echo '<p>Snapshots are still in development. If you see a bug or just have an idea how to make the tool better, please let us know <a href="https://twitter.com/WebFactoryLtd" target="_blank">@webfactoryltd</a> or <a href="mailto:wpreset@webfactoryltd.com?subject=WPR%20DB%20Snapshots%20Feedback">email us</a>. Thank you!</p>';
1010
-
1011
  $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1012
  if (is_array($table_status)) {
1013
  foreach ($table_status as $index => $table) {
@@ -1035,7 +1102,7 @@ class WP_Reset {
1035
  }
1036
  echo ' totaling ' . $this->format_size($tbl_size) .' in ' . number_format($tbl_rows) . ' rows.</p>';
1037
  }
1038
-
1039
  echo '';
1040
  echo '</div>';
1041
 
@@ -1077,11 +1144,11 @@ class WP_Reset {
1077
 
1078
  echo '</div>';
1079
  } // tab_snapshots
1080
-
1081
-
1082
  /**
1083
  * Echoes content for sponsor tab
1084
- *
1085
  * @return null
1086
  */
1087
  private function tab_geoip() {
@@ -1095,25 +1162,25 @@ class WP_Reset {
1095
  echo '<p>' . __('IP addresses are boring and don\'t mean much to people. However, they can easily be transformed into a huge source of data by using a GeoIP service. From filtering and segmenting users to providing a better UX - geographical data can enhance any web app! See an <a href="https://wpreset.com/geoip-transform-boring-data-better-user-experience/" target="_blank">example</a> we recently wrote about.', 'wp-reset') . '</p>';
1096
  echo '<p><a href="https://ipgeolocation.io/ip-location" target="_blank" class="button">See what data is available for your IP address</a></p>';
1097
  echo '</div>';
1098
-
1099
  echo '<div class="card">';
1100
  echo '<h2>' . __('Get a free account', 'wp-reset') . '</h2>';
1101
  echo '<p>' . __('IP Geolocation knows how difficult it is to start any new project. That\'s why they offer <a href="https://ipgeolocation.io/signup" target="_blank">50,000 API requests per month for free</a>. No credit card required, no tricks - just register for an account and you can use the service. It\'s a great way to add value to any web project.' , 'wp-reset') . '</p>';
1102
  echo '<p><a href="https://ipgeolocation.io/signup" target="_blank" class="button">Get a FREE account with 50,000 API requests a month</a></p>';
1103
  echo '</div>';
1104
-
1105
  echo '<p>Permanently <a href="#" class="wpr-dismiss-notice" data-notice="geoip_tab">remove this tab</a>.</p>';
1106
  } // tab_geoip
1107
-
1108
 
1109
  /**
1110
  * Helper function for generating UTM tagged links
1111
- *
1112
  * @param string $placement Optional. UTM content param.
1113
  * @param string $page Optional. Page to link to.
1114
  * @param array $params Optional. Extra URL params.
1115
  * @param string $anchor Optional. URL anchor part.
1116
- *
1117
  * @return string
1118
  */
1119
  function generate_web_link($placement = '', $page = '/', $params = array(), $anchor = '') {
@@ -1140,21 +1207,50 @@ class WP_Reset {
1140
 
1141
  /**
1142
  * Returns all saved snapshots from DB
1143
- *
1144
  * @return array
1145
  */
1146
  function get_snapshots() {
1147
  $snapshots = get_option('wp-reset-snapshots', array());
1148
-
1149
  return $snapshots;
1150
  } // get_snapshots
1151
 
1152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1153
  /**
1154
  * Format file size to human readable string
1155
- *
1156
  * @param int $bytes Size in bytes to format.
1157
- *
1158
  * @return string
1159
  */
1160
  function format_size($bytes) {
@@ -1172,9 +1268,9 @@ class WP_Reset {
1172
 
1173
  /**
1174
  * Creates snapshot of current tables by copying them in the DB and saving metadata.
1175
- *
1176
  * @param int $name Optional. Name for the new snapshot.
1177
- *
1178
  * @return array|WP_Error Snapshot details in array on success, or error object on fail.
1179
  */
1180
  function do_create_snapshot($name = '') {
@@ -1189,7 +1285,7 @@ class WP_Reset {
1189
  }
1190
 
1191
  if ($name) {
1192
- $snapshot['name'] = substr(trim($name), 0, 64);
1193
  } else {
1194
  $snapshot['name'] = '';
1195
  }
@@ -1237,9 +1333,9 @@ class WP_Reset {
1237
 
1238
  /**
1239
  * Delete snapshot metadata and tables from DB
1240
- *
1241
  * @param string $uid Snapshot unique 6-char ID.
1242
- *
1243
  * @return bool|WP_Error True on success, or error object on fail.
1244
  */
1245
  function do_delete_snapshot($uid = '') {
@@ -1268,9 +1364,9 @@ class WP_Reset {
1268
 
1269
  /**
1270
  * Exports snapshot as SQL dump; saved in gzipped file in WP_CONTENT folder.
1271
- *
1272
  * @param string $uid Snapshot unique 6-char ID.
1273
- *
1274
  * @return string|WP_Error Export base filename, or error object on fail.
1275
  */
1276
  function do_export_snapshot($uid = '') {
@@ -1297,7 +1393,7 @@ class WP_Reset {
1297
 
1298
  $folder = wp_mkdir_p(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder);
1299
  if (!$folder) {
1300
- return new WP_Error(1, 'Unable to create wp-content/' . $this->snapshots_folder . '/ folder.');
1301
  }
1302
 
1303
  $world_dumper->dump(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/wp-reset-snapshot-' . $uid . '.sql.gz', $uid . '_');
@@ -1311,9 +1407,9 @@ class WP_Reset {
1311
 
1312
  /**
1313
  * Replace current tables with ones in snapshot.
1314
- *
1315
  * @param string $uid Snapshot unique 6-char ID.
1316
- *
1317
  * @return bool|WP_Error True on success, or error object on fail.
1318
  */
1319
  function do_restore_snapshot($uid = '') {
@@ -1370,9 +1466,9 @@ class WP_Reset {
1370
 
1371
  /**
1372
  * Verifies snapshot integrity by comparing metadata and data in DB
1373
- *
1374
  * @param string $uid Snapshot unique 6-char ID.
1375
- *
1376
  * @return bool|WP_Error True on success, or error object on fail.
1377
  */
1378
  function verify_snapshot_integrity($uid) {
@@ -1420,9 +1516,9 @@ class WP_Reset {
1420
 
1421
  /**
1422
  * Compares a selected snapshot with the current table set in DB
1423
- *
1424
  * @param string $uid Snapshot unique 6-char ID.
1425
- *
1426
  * @return string|WP_Error Formatted table with details on success, or error object on fail.
1427
  */
1428
  function do_compare_snapshots($uid) {
@@ -1590,25 +1686,25 @@ class WP_Reset {
1590
  $out2 .= '<table class="table_diff">';
1591
  $out2 .= '<tr><td style="width: 100px;"><b>Option Name</b></td><td><b>Current Value</b></td><td><b>Snapshot Value</b></td></tr>';
1592
  foreach ($diff_rows as $row) {
1593
- $out2 .= '<tr>';
1594
  $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1595
  $out2 .= '<td>' . (empty($row->current_value)? '<i>empty</i>': $row->current_value) . '</td>';
1596
  $out2 .= '<td>' . (empty($row->snapshot_value)? '<i>empty</i>': $row->snapshot_value) . '</td>';
1597
- $out2 .= '</tr>';
1598
  } // foreach
1599
  foreach ($only_current as $row) {
1600
- $out2 .= '<tr>';
1601
  $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1602
  $out2 .= '<td>' . (empty($row->current_value)? '<i>empty</i>': $row->current_value) . '</td>';
1603
  $out2 .= '<td><i>not found in snapshot</i></td>';
1604
- $out2 .= '</tr>';
1605
  } // foreach
1606
  foreach ($only_current as $row) {
1607
- $out2 .= '<tr>';
1608
  $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1609
  $out2 .= '<td><i>not found in current tables</i></td>';
1610
  $out2 .= '<td>' . (empty($row->snapshot_value)? '<i>empty</i>': $row->snapshot_value) . '</td>';
1611
- $out2 .= '</tr>';
1612
  } // foreach
1613
  $out2 .= '</table>';
1614
  } else {
@@ -1628,7 +1724,7 @@ class WP_Reset {
1628
 
1629
  /**
1630
  * Generates a unique 6-char snapshot ID; verified non-existing
1631
- *
1632
  * @return string
1633
  */
1634
  function generate_snapshot_uid() {
@@ -1647,14 +1743,84 @@ class WP_Reset {
1647
  if ($cnt == 30) {
1648
  $uid = false;
1649
  }
1650
-
1651
  return $uid;
1652
  } // generate_snapshot_uid
1653
 
1654
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1655
  /**
1656
  * Clean up on uninstall; no action on deactive at the moment
1657
- *
1658
  * @return null
1659
  */
1660
  static function uninstall() {
@@ -1665,15 +1831,15 @@ class WP_Reset {
1665
 
1666
  /**
1667
  * Disabled; we use singleton pattern so magic functions need to be disabled
1668
- *
1669
  * @return null
1670
  */
1671
  private function __clone() {}
1672
-
1673
 
1674
  /**
1675
  * Disabled; we use singleton pattern so magic functions need to be disabled
1676
- *
1677
  * @return null
1678
  */
1679
  private function __sleep() {}
@@ -1681,7 +1847,7 @@ class WP_Reset {
1681
 
1682
  /**
1683
  * Disabled; we use singleton pattern so magic functions need to be disabled
1684
- *
1685
  * @return null
1686
  */
1687
  private function __wakeup() {}
3
  Plugin Name: WP Reset
4
  Plugin URI: https://wpreset.com/
5
  Description: Reset the site to default installation values without modifying any files. Deletes all customizations and content.
6
+ Version: 1.45
7
  Author: WebFactory Ltd
8
  Author URI: https://www.webfactoryltd.com/
9
  Text Domain: wp-reset
49
 
50
  /**
51
  * Creates a new WP_Reset object and implements singleton
52
+ *
53
  * @return WP_Reset
54
  */
55
  static function getInstance() {
63
 
64
  /**
65
  * Initialize properties, hook to filters and actions
66
+ *
67
  * @return null
68
  */
69
  private function __construct() {
71
  $this->plugin_dir = plugin_dir_path(__FILE__);
72
  $this->plugin_url = plugin_dir_url(__FILE__);
73
  $this->load_options();
74
+
75
  add_action('admin_menu', array($this, 'admin_menu'));
76
  add_action('admin_init', array($this, 'do_all_actions'));
77
  add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
78
  add_action('wp_ajax_wp_reset_dismiss_notice', array($this, 'ajax_dismiss_notice'));
79
  add_action('wp_ajax_wp_reset_run_tool', array($this, 'ajax_run_tool'));
80
+
81
  add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'plugin_action_links'));
82
  add_filter('plugin_row_meta', array($this, 'plugin_meta_links'), 10, 2);
83
  add_filter('admin_footer_text', array($this, 'admin_footer_text'));
84
+ add_filter('install_plugins_table_api_args_featured', array($this, 'featured_plugins_tab'));
85
 
86
  $this->core_tables = array_map(function($tbl) { global $wpdb; return $wpdb->prefix . $tbl; }, $this->core_tables);
87
  } // __construct
88
 
89
+
90
  /**
91
  * Get plugin version from file header
92
+ *
93
  * @return string
94
  */
95
  function get_plugin_version() {
97
 
98
  return $plugin_data['version'];
99
  } // get_plugin_version
100
+
101
 
102
  /**
103
  * Load and prepare the options array
104
  * If needed create a new DB entry
105
+ *
106
  * @return array
107
  */
108
  private function load_options() {
128
  if ($change) {
129
  update_option('wp-reset', $options, true);
130
  }
131
+
132
  $this->options = $options;
133
  return $options;
134
  } // load_options
136
 
137
  /**
138
  * Get meta part of plugin options
139
+ *
140
  * @return array
141
  */
142
  function get_meta() {
146
 
147
  /**
148
  * Get all dismissed notices, or check for one specific notice
149
+ *
150
  * @param string $notice_name Optional. Check if specified notice is dismissed.
151
+ *
152
  * @return bool|array
153
  */
154
  function get_dismissed_notices($notice_name = '') {
155
  $notices = $this->options['dismissed_notices'];
156
+
157
  if (empty($notice_name)) {
158
  return $notices;
159
  } else {
168
 
169
  /**
170
  * Get options part of plugin options
171
+ *
172
  * todo: not completed
173
+ *
174
  * @param string $key Optional.
175
+ *
176
  * @return array
177
  */
178
  function get_options($key = '') {
182
 
183
  /**
184
  * Update plugin options, currently entire array
185
+ *
186
  * todo: this handles the entire options array although it should only do the options part - it's confusing
187
+ *
188
  * @param string $key Data to save.
189
+ * @param string $data Option key.
190
+ *
191
  * @return bool
192
  */
193
  function update_options($key, $data) {
197
  return $tmp;
198
  } // set_options
199
 
200
+
201
  /**
202
  * Add plugin menu entry under Tools menu
203
+ *
204
  * @return null
205
  */
206
  function admin_menu() {
210
 
211
  /**
212
  * Dismiss notice via AJAX call
213
+ *
214
  * @return null
215
  */
216
  function ajax_dismiss_notice() {
227
 
228
  /**
229
  * Dismiss notice by adding it to dismissed_notices options array
230
+ *
231
  * @param string $notice_name Notice to dismiss.
232
+ *
233
  * @return bool
234
  */
235
  function dismiss_notice($notice_name) {
246
 
247
  /**
248
  * Returns all WP pointers
249
+ *
250
  * @return array
251
  */
252
  function get_pointers() {
260
 
261
  /**
262
  * Enqueue CSS and JS files
263
+ *
264
  * @return null
265
  */
266
  function admin_enqueue_scripts($hook) {
288
  if ('tools_page_wp-reset' != $hook) {
289
  return;
290
  }
291
+
292
  $options = $this->get_options();
293
 
294
  $js_localize = array('undocumented_error' => __('An undocumented error has occured. Please refresh the page and try again.', 'wp-reset'),
331
  wp_dequeue_style('wpmegmaps-jqueryui');
332
  wp_dequeue_style('wp-botwatch-css');
333
  } // admin_enqueue_scripts
334
+
335
 
336
  /**
337
  * Check if WP-CLI is available and running
338
+ *
339
  * @return bool
340
  */
341
  function is_cli_running() {
349
 
350
  /**
351
  * Deletes all transients.
352
+ *
353
  * @return int Number of deleted transient DB entries
354
  */
355
  function do_delete_transients() {
356
  global $wpdb;
357
+
358
  $count = $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '\_transient\_%' OR option_name LIKE '\_site\_transient\_%'");
359
+
360
  return $count;
361
  } // do_delete_transients
362
+
363
 
364
  /**
365
  * Deletes all files in uploads folder.
366
+ *
367
  * @return int Number of deleted files and folders.
368
  */
369
  function do_delete_uploads() {
370
  $upload_dir = wp_get_upload_dir();
371
 
372
  $this->delete_folder($upload_dir['basedir'], $upload_dir['basedir']);
373
+
374
  return $this->delete_count;
375
  } // do_delete_uploads
376
 
377
 
378
  /**
379
  * Recursively deletes a folder
380
+ *
381
  * @param string $folder Recursive param.
382
  * @param string $base_folder Base folder.
383
+ *
384
  * @return bool
385
  */
386
  private function delete_folder($folder, $base_folder) {
408
  /**
409
  * Deactivate and delete all plugins
410
  *
411
+ * @param bool $keep_wp_reset Keep WP Reset active and installed
412
  * @param bool $silent_deactivate Skip individual plugin deactivation functions when deactivating
413
+ *
414
  * @return int Number of deleted plugins.
415
  */
416
  function do_delete_plugins($keep_wp_reset = true, $silent_deactivate = false) {
445
  * Delete all themes
446
  *
447
  * @param bool $keep_default_theme Keep default theme
448
+ *
449
  * @return int Number of deleted themes.
450
  */
451
  function do_delete_themes($keep_default_theme = true) {
459
  foreach ($all_themes as $theme_slug => $theme_details) {
460
  $res = delete_theme($theme_slug);
461
  }
462
+
463
  if (false == $keep_default_theme) {
464
  update_option('template', '');
465
  update_option('stylesheet', '');
469
  } // do_delete_themes
470
 
471
 
472
+ /**
473
+ * Truncate custom tables
474
+ *
475
+ * @return int Number of truncated tables.
476
+ */
477
+ function do_truncate_custom_tables() {
478
+ global $wpdb;
479
+ $custom_tables = $this->get_custom_tables();
480
+
481
+ foreach ($custom_tables as $tbl) {
482
+ $wpdb->query('TRUNCATE TABLE ' . $tbl['name']);
483
+ } // foreach
484
+
485
+ return sizeof($custom_tables);
486
+ } // do_truncate_custom_tables
487
+
488
+
489
+ /**
490
+ * Drop custom tables
491
+ *
492
+ * @return int Number of dropped tables.
493
+ */
494
+ function do_drop_custom_tables() {
495
+ global $wpdb;
496
+ $custom_tables = $this->get_custom_tables();
497
+
498
+ foreach ($custom_tables as $tbl) {
499
+ $wpdb->query('DROP TABLE IF EXISTS ' . $tbl['name']);
500
+ } // foreach
501
+
502
+ return sizeof($custom_tables);
503
+ } // do_drop_custom_tables
504
+
505
+
506
  /**
507
  * Run one tool via AJAX call
508
+ *
509
  * @return null
510
  */
511
  function ajax_run_tool() {
513
 
514
  $tool = trim(@$_GET['tool']);
515
  $extra_data = trim(@$_GET['extra_data']);
516
+
517
  if ($tool == 'delete_transients') {
518
  $cnt = $this->do_delete_transients();
519
  wp_send_json_success($cnt);
526
  } elseif ($tool == 'delete_uploads') {
527
  $cnt = $this->do_delete_uploads();
528
  wp_send_json_success($cnt);
529
+ } elseif ($tool == 'drop_custom_tables') {
530
+ $cnt = $this->do_drop_custom_tables();
531
+ wp_send_json_success($cnt);
532
+ } elseif ($tool == 'truncate_custom_tables') {
533
+ $cnt = $this->do_truncate_custom_tables();
534
+ wp_send_json_success($cnt);
535
  } elseif ($tool == 'delete_snapshot') {
536
  $res = $this->do_delete_snapshot($extra_data);
537
  if (is_wp_error($res)) {
578
  * Reinstall / reset the WP site
579
  * There are no failsafes in the function - it reinstalls when called
580
  * Redirects when done
581
+ *
582
  * @param array $params Optional.
583
+ *
584
  * @return null
585
  */
586
  function do_reinstall($params = array()) {
604
  $siteurl = get_option('siteurl');
605
  $home = get_option('home');
606
  $snapshots = $this->get_snapshots();
607
+
608
  $active_plugins = get_option('active_plugins');
609
  $active_theme = wp_get_theme();
610
 
665
  if (!empty($params['reactivate_plugins'])) {
666
  foreach ($active_plugins as $plugin_file) {
667
  activate_plugin($plugin_file);
668
+ }
669
  }
670
 
671
  if (!$this->is_cli_running()) {
678
  exit;
679
  }
680
  } // do_reinstall
681
+
682
 
683
  /**
684
  * Checks wp_reset post value and performs all actions
685
  * todo: handle messages for various actions
686
+ *
687
  * @return null|bool
688
  */
689
  function do_all_actions() {
701
  add_settings_error('wp-reset', 'bad-nonce', __('Something went wrong. Please refresh the page and try again.', 'wp-reset'), 'error');
702
  return false;
703
  }
704
+
705
  // check confirmation code
706
  if (true === isset($_POST['wp_reset_confirm']) && 'reset' !== $_POST['wp_reset_confirm']) {
707
  add_settings_error('wp-reset', 'bad-confirm', __('<b>Invalid confirmation code.</b> Please type "reset" in the confirmation field.', 'wp-reset'), 'error');
721
 
722
 
723
  /**
724
+ * Add "Open WP Reset Tools" action link to plugins table, left part
725
+ *
726
  * @param array $links Initial list of links.
727
+ *
728
  * @return array
729
  */
730
  function plugin_action_links($links) {
731
+ $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>';
732
 
733
  array_unshift($links, $settings_link);
734
 
735
  return $links;
736
  } // plugin_action_links
737
+
738
+
739
  /**
740
  * Add links to plugin's description in plugins table
741
+ *
742
  * @param array $links Initial list of links.
743
  * @param string $file Basename of current plugin.
744
+ *
745
  * @return array
746
  */
747
  function plugin_meta_links($links, $file) {
748
  if ($file !== plugin_basename(__FILE__)) {
749
  return $links;
750
  }
751
+
752
  $support_link = '<a target="_blank" href="https://wordpress.org/support/plugin/wp-reset" title="' . __('Get help', 'wp-reset') . '">' . __('Support', 'wp-reset') . '</a>';
753
  $home_link = '<a target="_blank" href="' . $this->generate_web_link('plugins-table-right') . '" title="' . __('Plugin Homepage', 'wp-reset') . '">' . __('Plugin Homepage', 'wp-reset') . '</a>';
754
  $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>';
755
+
756
  $links[] = $support_link;
757
  $links[] = $home_link;
758
  $links[] = $rate_link;
759
+
760
  return $links;
761
  } // plugin_meta_links
762
 
763
 
764
  /**
765
  * Test if we're on WPR's admin page
766
+ *
767
  * @return bool
768
  */
769
  function is_plugin_page() {
779
 
780
  /**
781
  * Add powered by text in admin footer
782
+ *
783
  * @param string $text Default footer text.
784
+ *
785
  * @return string
786
  */
787
  function admin_footer_text($text) {
797
 
798
  /**
799
  * Loads plugin's translated strings
800
+ *
801
  * @return null
802
  */
803
  function load_textdomain() {
807
 
808
  /**
809
  * Inform the user that WordPress has been successfully reset
810
+ *
811
  * @return null
812
  */
813
  function notice_successfull_reset() {
819
 
820
  /**
821
  * Outputs complete plugin's admin page
822
+ *
823
  * @return null
824
  */
825
  function plugin_page() {
845
  echo '</div>';
846
  $notice_shown = true;
847
  }
848
+
849
  if ((!empty($meta['reset_count']) || !empty($snapshots)) && false === $notice_shown && false == $this->get_dismissed_notices('rate')) {
850
  echo '<div class="card notice-wrapper">';
851
  echo '<h2>' . __('Please help us keep the plugin free &amp; up-to-date', 'wp-reset') . '</h2>';
854
  echo '</div>';
855
  $notice_shown = true;
856
  }
857
+
858
  // Tidy Repo ad
859
  // disabled for now
860
  if (false && false === $notice_shown && $meta['reset_count'] >= 2 && false == $this->get_dismissed_notices('tidy')) {
886
  echo '<div style="display: none;" id="tab-tools">';
887
  $this->tab_tools();
888
  echo '</div>';
889
+
890
  echo '<div style="display: none;" id="tab-snapshots">';
891
  $this->tab_snapshots();
892
  echo '</div>';
894
  echo '<div style="display: none;" id="tab-support">';
895
  $this->tab_support();
896
  echo '</div>';
897
+
898
  if (empty($notices['geoip_tab'])) {
899
  echo '<div style="display: none;" id="tab-geoip">';
900
  $this->tab_geoip();
906
  echo '</form>';
907
  echo '</div>'; // wrap
908
  } // plugin_page
909
+
910
 
911
  /**
912
  * Echoes content for reset tab
913
+ *
914
  * @return null
915
  */
916
  private function tab_reset() {
961
  echo '<div class="card">';
962
  echo '<h2>' . __('Reset', 'wp-reset') . '</h2>';
963
  echo '<p>' . __('Type <b>reset</b> in the confirmation field to confirm the reset and then click the "Reset WordPress" button. <b>There is NO UNDO. No backups are made by WP Reset.</b>', 'wp-reset') . '</p>';
964
+
965
  wp_nonce_field('wp-reset');
966
  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;';
967
  echo '<input id="wp_reset_submit" type="button" class="button-primary" value="' . __('Reset WordPress', 'wp-reset') . '"></p>';
971
 
972
  /**
973
  * Echoes content for tools tab
974
+ *
975
  * @return null
976
  */
977
  private function tab_tools() {
978
+ global $wpdb;
979
 
980
  echo '<div class="card">';
981
+ echo '<h2>' . __('Delete Transients', 'wp-reset') . '</h2>';
982
+ echo '<p>' . __('All transient related database entries will be deleted. Including expired and non-expired transients, and orphaned transient timeout entries.<br><b>There is NO UNDO. WP Reset does not make any backups.</b>', 'wp-reset') . '</p>';
983
+ echo '<p><a 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. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n transient database entries have been deleted." class="button button-delete" href="#" id="delete-transients">Delete all transients</a></p>';
984
  echo '</div>';
985
 
986
  $upload_dir = wp_upload_dir(date('Y/m'), true);
987
+ $upload_dir['basedir'] = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $upload_dir['basedir']);
988
 
989
  echo '<div class="card">';
990
+ echo '<h2>' . __('Clean Uploads Folder', 'wp-reset') . '</h2>';
991
+ 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 backups.</b>', 'wp-reset') . '</p>';
992
  if (false != $upload_dir['error']) {
993
  echo '<p><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>';
994
  } else {
995
+ echo '<p><a 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 will not make any backups." data-text-done="%n files &amp; folders have been deleted." class="button button-delete" href="#" id="delete-uploads">Delete all files &amp; folders in uploads folder</a></p>';
996
  }
997
  echo '</div>';
998
 
999
+ $theme = wp_get_theme();
1000
+
1001
+ echo '<div class="card">';
1002
+ echo '<h2>' . __('Delete Themes', 'wp-reset') . '</h2>';
1003
+ 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 backups.</b>', 'wp-reset') . '</p>';
1004
+ echo '<p><a 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 will not make any backups." data-text-done="%n themes have been deleted." class="button button-delete" href="#" id="delete-themes">Delete all themes</a></p>';
1005
+ echo '</div>';
1006
+
1007
  echo '<div class="card">';
1008
+ echo '<h2>' . __('Delete Plugins', 'wp-reset') . '</h2>';
1009
+ 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 backups.</b>', 'wp-reset') . '</p>';
1010
+ echo '<p><a 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 will not make any backups." data-text-done="%n plugins have been deleted." class="button button-delete" href="#" id="delete-plugins">Delete plugins</a></p>';
1011
  echo '</div>';
1012
 
1013
+ $custom_tables = $this->get_custom_tables();
1014
+
1015
  echo '<div class="card">';
1016
+ echo '<h2>' . __('Empty or Delete Custom Tables', 'wp-reset') . '</h2>';
1017
+ 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><b>There is NO UNDO. WP Reset does not make any backups.</b></p>', 'wp-reset');
1018
+ if ($custom_tables) {
1019
+ echo '<p>' . __('The following ' . sizeof($custom_tables) . ' custom tables are affected by this tool: ');
1020
+ foreach ($custom_tables as $tbl) {
1021
+ echo '<code>' . $tbl['name'] . '</code>';
1022
+ if (next($custom_tables)) {
1023
+ echo ', ';
1024
+ }
1025
+ } // foreach
1026
+ echo '.</p>';
1027
+ $custom_tables_btns = '';
1028
+ } else {
1029
+ echo '<p>' . __('There are no custom tables. There\'s nothing for this tool to empty or delete.', 'wp-reset') . '</p>';
1030
+ $custom_tables_btns = ' disabled';
1031
+ }
1032
+ echo '<p><a 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. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n custom tables have been emptied." class="button button-delete' . $custom_tables_btns . '" href="#" id="truncate-custom-tables">Empty (truncate) custom tables</a>&nbsp; &nbsp;';
1033
+ echo '<a 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. There is NO UNDO. WP Reset will not make any backups." data-text-done="%n custom tables have been deleted." class="button button-delete' . $custom_tables_btns . '" href="#" id="drop-custom-tables">Delete (drop) custom tables</a></p>';
1034
+
1035
  echo '</div>';
1036
  } // tab_tools
1037
 
1038
 
1039
  /**
1040
  * Echoes content for support tab
1041
+ *
1042
  * @return null
1043
  */
1044
  private function tab_support() {
1057
  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. Thank you!', 'wp-reset') . '</p>';
1058
  echo '</div>';
1059
  } // tab_support
1060
+
1061
+
1062
  /**
1063
  * Echoes content for snapshots tab
1064
+ *
1065
  * @return null
1066
  */
1067
  private function tab_snapshots() {
1074
  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.<br>
1075
  Snapshots are primarily a development tool. Although they can be used for backups (and downloaded), we suggest finding a more suitable tool for live sites, such as <a href="https://wordpress.org/plugins/updraftplus/" target="_blank">UpdraftPlus</a>. 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.<br>Restoring a snapshot does not affect other snapshots, or WP Reset settings.</p>';
1076
  echo '<p>Snapshots are still in development. If you see a bug or just have an idea how to make the tool better, please let us know <a href="https://twitter.com/WebFactoryLtd" target="_blank">@webfactoryltd</a> or <a href="mailto:wpreset@webfactoryltd.com?subject=WPR%20DB%20Snapshots%20Feedback">email us</a>. Thank you!</p>';
1077
+
1078
  $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1079
  if (is_array($table_status)) {
1080
  foreach ($table_status as $index => $table) {
1102
  }
1103
  echo ' totaling ' . $this->format_size($tbl_size) .' in ' . number_format($tbl_rows) . ' rows.</p>';
1104
  }
1105
+
1106
  echo '';
1107
  echo '</div>';
1108
 
1144
 
1145
  echo '</div>';
1146
  } // tab_snapshots
1147
+
1148
+
1149
  /**
1150
  * Echoes content for sponsor tab
1151
+ *
1152
  * @return null
1153
  */
1154
  private function tab_geoip() {
1162
  echo '<p>' . __('IP addresses are boring and don\'t mean much to people. However, they can easily be transformed into a huge source of data by using a GeoIP service. From filtering and segmenting users to providing a better UX - geographical data can enhance any web app! See an <a href="https://wpreset.com/geoip-transform-boring-data-better-user-experience/" target="_blank">example</a> we recently wrote about.', 'wp-reset') . '</p>';
1163
  echo '<p><a href="https://ipgeolocation.io/ip-location" target="_blank" class="button">See what data is available for your IP address</a></p>';
1164
  echo '</div>';
1165
+
1166
  echo '<div class="card">';
1167
  echo '<h2>' . __('Get a free account', 'wp-reset') . '</h2>';
1168
  echo '<p>' . __('IP Geolocation knows how difficult it is to start any new project. That\'s why they offer <a href="https://ipgeolocation.io/signup" target="_blank">50,000 API requests per month for free</a>. No credit card required, no tricks - just register for an account and you can use the service. It\'s a great way to add value to any web project.' , 'wp-reset') . '</p>';
1169
  echo '<p><a href="https://ipgeolocation.io/signup" target="_blank" class="button">Get a FREE account with 50,000 API requests a month</a></p>';
1170
  echo '</div>';
1171
+
1172
  echo '<p>Permanently <a href="#" class="wpr-dismiss-notice" data-notice="geoip_tab">remove this tab</a>.</p>';
1173
  } // tab_geoip
1174
+
1175
 
1176
  /**
1177
  * Helper function for generating UTM tagged links
1178
+ *
1179
  * @param string $placement Optional. UTM content param.
1180
  * @param string $page Optional. Page to link to.
1181
  * @param array $params Optional. Extra URL params.
1182
  * @param string $anchor Optional. URL anchor part.
1183
+ *
1184
  * @return string
1185
  */
1186
  function generate_web_link($placement = '', $page = '/', $params = array(), $anchor = '') {
1207
 
1208
  /**
1209
  * Returns all saved snapshots from DB
1210
+ *
1211
  * @return array
1212
  */
1213
  function get_snapshots() {
1214
  $snapshots = get_option('wp-reset-snapshots', array());
1215
+
1216
  return $snapshots;
1217
  } // get_snapshots
1218
 
1219
 
1220
+ /**
1221
+ * Returns all custom table names, with prefix
1222
+ *
1223
+ * @return array
1224
+ */
1225
+ function get_custom_tables() {
1226
+ global $wpdb;
1227
+ $custom_tables = array();
1228
+
1229
+ $table_status = $wpdb->get_results('SHOW TABLE STATUS');
1230
+ if (is_array($table_status)) {
1231
+ foreach ($table_status as $index => $table) {
1232
+ if (0 !== stripos($table->Name, $wpdb->prefix)) {
1233
+ continue;
1234
+ }
1235
+ if (empty($table->Engine)) {
1236
+ continue;
1237
+ }
1238
+
1239
+ if (false === in_array($table->Name, $this->core_tables)) {
1240
+ $custom_tables[] = array('name' => $table->Name, 'rows' => $table->Rows, 'data_length' => $table->Data_length, 'index_length' => $table->Index_length);
1241
+ }
1242
+ } // foreach
1243
+ }
1244
+
1245
+ return $custom_tables;
1246
+ } // get_custom tables
1247
+
1248
+
1249
  /**
1250
  * Format file size to human readable string
1251
+ *
1252
  * @param int $bytes Size in bytes to format.
1253
+ *
1254
  * @return string
1255
  */
1256
  function format_size($bytes) {
1268
 
1269
  /**
1270
  * Creates snapshot of current tables by copying them in the DB and saving metadata.
1271
+ *
1272
  * @param int $name Optional. Name for the new snapshot.
1273
+ *
1274
  * @return array|WP_Error Snapshot details in array on success, or error object on fail.
1275
  */
1276
  function do_create_snapshot($name = '') {
1285
  }
1286
 
1287
  if ($name) {
1288
+ $snapshot['name'] = substr(trim($name), 0, 64);
1289
  } else {
1290
  $snapshot['name'] = '';
1291
  }
1333
 
1334
  /**
1335
  * Delete snapshot metadata and tables from DB
1336
+ *
1337
  * @param string $uid Snapshot unique 6-char ID.
1338
+ *
1339
  * @return bool|WP_Error True on success, or error object on fail.
1340
  */
1341
  function do_delete_snapshot($uid = '') {
1364
 
1365
  /**
1366
  * Exports snapshot as SQL dump; saved in gzipped file in WP_CONTENT folder.
1367
+ *
1368
  * @param string $uid Snapshot unique 6-char ID.
1369
+ *
1370
  * @return string|WP_Error Export base filename, or error object on fail.
1371
  */
1372
  function do_export_snapshot($uid = '') {
1393
 
1394
  $folder = wp_mkdir_p(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder);
1395
  if (!$folder) {
1396
+ return new WP_Error(1, 'Unable to create wp-content/' . $this->snapshots_folder . '/ folder.');
1397
  }
1398
 
1399
  $world_dumper->dump(trailingslashit(WP_CONTENT_DIR) . $this->snapshots_folder . '/wp-reset-snapshot-' . $uid . '.sql.gz', $uid . '_');
1407
 
1408
  /**
1409
  * Replace current tables with ones in snapshot.
1410
+ *
1411
  * @param string $uid Snapshot unique 6-char ID.
1412
+ *
1413
  * @return bool|WP_Error True on success, or error object on fail.
1414
  */
1415
  function do_restore_snapshot($uid = '') {
1466
 
1467
  /**
1468
  * Verifies snapshot integrity by comparing metadata and data in DB
1469
+ *
1470
  * @param string $uid Snapshot unique 6-char ID.
1471
+ *
1472
  * @return bool|WP_Error True on success, or error object on fail.
1473
  */
1474
  function verify_snapshot_integrity($uid) {
1516
 
1517
  /**
1518
  * Compares a selected snapshot with the current table set in DB
1519
+ *
1520
  * @param string $uid Snapshot unique 6-char ID.
1521
+ *
1522
  * @return string|WP_Error Formatted table with details on success, or error object on fail.
1523
  */
1524
  function do_compare_snapshots($uid) {
1686
  $out2 .= '<table class="table_diff">';
1687
  $out2 .= '<tr><td style="width: 100px;"><b>Option Name</b></td><td><b>Current Value</b></td><td><b>Snapshot Value</b></td></tr>';
1688
  foreach ($diff_rows as $row) {
1689
+ $out2 .= '<tr>';
1690
  $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1691
  $out2 .= '<td>' . (empty($row->current_value)? '<i>empty</i>': $row->current_value) . '</td>';
1692
  $out2 .= '<td>' . (empty($row->snapshot_value)? '<i>empty</i>': $row->snapshot_value) . '</td>';
1693
+ $out2 .= '</tr>';
1694
  } // foreach
1695
  foreach ($only_current as $row) {
1696
+ $out2 .= '<tr>';
1697
  $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1698
  $out2 .= '<td>' . (empty($row->current_value)? '<i>empty</i>': $row->current_value) . '</td>';
1699
  $out2 .= '<td><i>not found in snapshot</i></td>';
1700
+ $out2 .= '</tr>';
1701
  } // foreach
1702
  foreach ($only_current as $row) {
1703
+ $out2 .= '<tr>';
1704
  $out2 .= '<td style="width: 100px;">' . $row->option_name . '</td>';
1705
  $out2 .= '<td><i>not found in current tables</i></td>';
1706
  $out2 .= '<td>' . (empty($row->snapshot_value)? '<i>empty</i>': $row->snapshot_value) . '</td>';
1707
+ $out2 .= '</tr>';
1708
  } // foreach
1709
  $out2 .= '</table>';
1710
  } else {
1724
 
1725
  /**
1726
  * Generates a unique 6-char snapshot ID; verified non-existing
1727
+ *
1728
  * @return string
1729
  */
1730
  function generate_snapshot_uid() {
1743
  if ($cnt == 30) {
1744
  $uid = false;
1745
  }
1746
+
1747
  return $uid;
1748
  } // generate_snapshot_uid
1749
 
1750
 
1751
+ /**
1752
+ * Helper function for adding plugins to featured list
1753
+ *
1754
+ * @return array
1755
+ */
1756
+ function featured_plugins_tab($args) {
1757
+ add_filter('plugins_api_result', array($this, 'plugins_api_result'), 10, 3);
1758
+
1759
+ return $args;
1760
+ } // featured_plugins_tab
1761
+
1762
+
1763
+ /**
1764
+ * Add single plugin to featured list
1765
+ *
1766
+ * @return object
1767
+ */
1768
+ function add_plugin_featured($plugin_slug, $res) {
1769
+ // check if plugin is alredy on the list
1770
+ if (is_array($res->plugins)) {
1771
+ foreach ($res->plugins as $plugin) {
1772
+ if ($plugin->slug == $plugin_slug) {
1773
+ return $res;
1774
+ }
1775
+ } // foreach
1776
+ }
1777
+
1778
+ if ($plugin_info = get_transient('wf-plugin-info-' . $plugin_slug)) {
1779
+ $tmp1 = array_slice($res->plugins, 0, 2, false);
1780
+ $tmp2 = array_slice($res->plugins, 2, sizeof($res->plugins) - 2, false);
1781
+ $res->plugins = array_merge($tmp1, array($plugin_info), $tmp2);
1782
+ } else {
1783
+ $plugin_info = plugins_api('plugin_information', array(
1784
+ 'slug' => $plugin_slug,
1785
+ 'is_ssl' => is_ssl(),
1786
+ 'fields' => array(
1787
+ 'banners' => true,
1788
+ 'reviews' => true,
1789
+ 'downloaded' => true,
1790
+ 'active_installs' => true,
1791
+ 'icons' => true,
1792
+ 'short_description' => true,
1793
+ )
1794
+ ));
1795
+ if (!is_wp_error($plugin_info)) {
1796
+ $tmp1 = array_slice($res->plugins, 0, 2, false);
1797
+ $tmp2 = array_slice($res->plugins, 2, sizeof($res->plugins) - 2, false);
1798
+ $res->plugins = array_merge($tmp1, array($plugin_info), $tmp2);
1799
+ set_transient('wf-plugin-info-' . $plugin_slug, $plugin_info, DAY_IN_SECONDS * 7);
1800
+ }
1801
+ }
1802
+
1803
+ return $res;
1804
+ } // add_plugin_featured
1805
+
1806
+
1807
+ /**
1808
+ * Add plugins to featured plugins list
1809
+ *
1810
+ * @return object
1811
+ */
1812
+ function plugins_api_result($res, $action, $args) {
1813
+ remove_filter('plugins_api_result', array($this, 'plugins_api_result'), 10, 3);
1814
+
1815
+ $res = $this->add_plugin_featured('security-ninja', $res);
1816
+
1817
+ return $res;
1818
+ } // plugins_api_result
1819
+
1820
+
1821
  /**
1822
  * Clean up on uninstall; no action on deactive at the moment
1823
+ *
1824
  * @return null
1825
  */
1826
  static function uninstall() {
1831
 
1832
  /**
1833
  * Disabled; we use singleton pattern so magic functions need to be disabled
1834
+ *
1835
  * @return null
1836
  */
1837
  private function __clone() {}
1838
+
1839
 
1840
  /**
1841
  * Disabled; we use singleton pattern so magic functions need to be disabled
1842
+ *
1843
  * @return null
1844
  */
1845
  private function __sleep() {}
1847
 
1848
  /**
1849
  * Disabled; we use singleton pattern so magic functions need to be disabled
1850
+ *
1851
  * @return null
1852
  */
1853
  private function __wakeup() {}