1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* REST API WC System Status Tools Controller |
4
|
|
|
* |
5
|
|
|
* Handles requests to the /system_status/tools/* endpoints. |
6
|
|
|
* |
7
|
|
|
* @author WooThemes |
8
|
|
|
* @category API |
9
|
|
|
* @package WooCommerce/API |
10
|
|
|
* @since 2.7.0 |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
if ( ! defined( 'ABSPATH' ) ) { |
14
|
|
|
exit; |
15
|
|
|
} |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* @package WooCommerce/API |
19
|
|
|
* @extends WC_REST_Controller |
20
|
|
|
*/ |
21
|
|
|
class WC_REST_System_Status_Tools_Controller extends WC_REST_Controller { |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Endpoint namespace. |
25
|
|
|
* |
26
|
|
|
* @var string |
27
|
|
|
*/ |
28
|
|
|
protected $namespace = 'wc/v1'; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Route base. |
32
|
|
|
* |
33
|
|
|
* @var string |
34
|
|
|
*/ |
35
|
|
|
protected $rest_base = 'system_status/tools'; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Register the routes for /system_status/tools/*. |
39
|
|
|
*/ |
40
|
|
View Code Duplication |
public function register_routes() { |
|
|
|
|
41
|
|
|
register_rest_route( $this->namespace, '/' . $this->rest_base, array( |
42
|
|
|
array( |
43
|
|
|
'methods' => WP_REST_Server::READABLE, |
44
|
|
|
'callback' => array( $this, 'get_items' ), |
45
|
|
|
'permission_callback' => array( $this, 'get_items_permissions_check' ), |
46
|
|
|
'args' => $this->get_collection_params(), |
47
|
|
|
), |
48
|
|
|
'schema' => array( $this, 'get_public_item_schema' ), |
49
|
|
|
) ); |
50
|
|
|
|
51
|
|
|
register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\w-]+)', array( |
52
|
|
|
array( |
53
|
|
|
'methods' => WP_REST_Server::READABLE, |
54
|
|
|
'callback' => array( $this, 'get_item' ), |
55
|
|
|
'permission_callback' => array( $this, 'get_item_permissions_check' ), |
56
|
|
|
), |
57
|
|
|
array( |
58
|
|
|
'methods' => WP_REST_Server::EDITABLE, |
59
|
|
|
'callback' => array( $this, 'update_item' ), |
60
|
|
|
'permission_callback' => array( $this, 'update_item_permissions_check' ), |
61
|
|
|
'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), |
62
|
|
|
), |
63
|
|
|
'schema' => array( $this, 'get_public_item_schema' ), |
64
|
|
|
) ); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Check whether a given request has permission to view system status tools. |
69
|
|
|
* |
70
|
|
|
* @param WP_REST_Request $request Full details about the request. |
71
|
|
|
* @return WP_Error|boolean |
72
|
|
|
*/ |
73
|
|
|
public function get_items_permissions_check( $request ) { |
74
|
|
|
if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) { |
75
|
|
|
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); |
76
|
|
|
} |
77
|
|
|
return true; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Check whether a given request has permission to view a specific system status tool. |
82
|
|
|
* |
83
|
|
|
* @param WP_REST_Request $request Full details about the request. |
84
|
|
|
* @return WP_Error|boolean |
85
|
|
|
*/ |
86
|
|
|
public function get_item_permissions_check( $request ) { |
87
|
|
|
if ( ! wc_rest_check_manager_permissions( 'system_status', 'read' ) ) { |
88
|
|
|
return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); |
89
|
|
|
} |
90
|
|
|
return true; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Check whether a given request has permission to execute a specific system status tool. |
95
|
|
|
* |
96
|
|
|
* @param WP_REST_Request $request Full details about the request. |
97
|
|
|
* @return WP_Error|boolean |
98
|
|
|
*/ |
99
|
|
|
public function update_item_permissions_check( $request ) { |
100
|
|
|
if ( ! wc_rest_check_manager_permissions( 'system_status', 'edit' ) ) { |
101
|
|
|
return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); |
102
|
|
|
} |
103
|
|
|
return true; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* A list of avaiable tools for use in the system status section. |
108
|
|
|
* 'button' becomes 'action' in the API. |
109
|
|
|
* |
110
|
|
|
* @return array |
111
|
|
|
*/ |
112
|
|
|
public function get_tools() { |
113
|
|
|
$tools = array( |
114
|
|
|
'clear_transients' => array( |
115
|
|
|
'name' => __( 'WC Transients', 'woocommerce' ), |
116
|
|
|
'button' => __( 'Clear transients', 'woocommerce' ), |
117
|
|
|
'desc' => __( 'This tool will clear the product/shop transients cache.', 'woocommerce' ), |
118
|
|
|
), |
119
|
|
|
'clear_expired_transients' => array( |
120
|
|
|
'name' => __( 'Expired Transients', 'woocommerce' ), |
121
|
|
|
'button' => __( 'Clear expired transients', 'woocommerce' ), |
122
|
|
|
'desc' => __( 'This tool will clear ALL expired transients from WordPress.', 'woocommerce' ), |
123
|
|
|
), |
124
|
|
|
'recount_terms' => array( |
125
|
|
|
'name' => __( 'Term counts', 'woocommerce' ), |
126
|
|
|
'button' => __( 'Recount terms', 'woocommerce' ), |
127
|
|
|
'desc' => __( 'This tool will recount product terms - useful when changing your settings in a way which hides products from the catalog.', 'woocommerce' ), |
128
|
|
|
), |
129
|
|
|
'reset_roles' => array( |
130
|
|
|
'name' => __( 'Capabilities', 'woocommerce' ), |
131
|
|
|
'button' => __( 'Reset capabilities', 'woocommerce' ), |
132
|
|
|
'desc' => __( 'This tool will reset the admin, customer and shop_manager roles to default. Use this if your users cannot access all of the WooCommerce admin pages.', 'woocommerce' ), |
133
|
|
|
), |
134
|
|
|
'clear_sessions' => array( |
135
|
|
|
'name' => __( 'Customer Sessions', 'woocommerce' ), |
136
|
|
|
'button' => __( 'Clear all sessions', 'woocommerce' ), |
137
|
|
|
'desc' => __( '<strong class="red">Warning:</strong> This tool will delete all customer session data from the database, including any current live carts.', 'woocommerce' ), |
138
|
|
|
), |
139
|
|
|
'install_pages' => array( |
140
|
|
|
'name' => __( 'Install WooCommerce Pages', 'woocommerce' ), |
141
|
|
|
'button' => __( 'Install pages', 'woocommerce' ), |
142
|
|
|
'desc' => __( '<strong class="red">Note:</strong> This tool will install all the missing WooCommerce pages. Pages already defined and set up will not be replaced.', 'woocommerce' ), |
143
|
|
|
), |
144
|
|
|
'delete_taxes' => array( |
145
|
|
|
'name' => __( 'Delete all WooCommerce tax rates', 'woocommerce' ), |
146
|
|
|
'button' => __( 'Delete ALL tax rates', 'woocommerce' ), |
147
|
|
|
'desc' => __( '<strong class="red">Note:</strong> This option will delete ALL of your tax rates, use with caution.', 'woocommerce' ), |
148
|
|
|
), |
149
|
|
|
'reset_tracking' => array( |
150
|
|
|
'name' => __( 'Reset Usage Tracking Settings', 'woocommerce' ), |
151
|
|
|
'button' => __( 'Reset usage tracking settings', 'woocommerce' ), |
152
|
|
|
'desc' => __( 'This will reset your usage tracking settings, causing it to show the opt-in banner again and not sending any data.', 'woocommerce' ), |
153
|
|
|
) |
154
|
|
|
); |
155
|
|
|
|
156
|
|
|
return apply_filters( 'woocommerce_debug_tools', $tools ); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Get a list of system status tools. |
161
|
|
|
* |
162
|
|
|
* @param WP_REST_Request $request Full details about the request. |
163
|
|
|
* @return WP_Error|WP_REST_Response |
164
|
|
|
*/ |
165
|
|
|
public function get_items( $request ) { |
166
|
|
|
$tools = array(); |
167
|
|
|
foreach ( $this->get_tools() as $id => $tool ) { |
168
|
|
|
$tools[] = $this->prepare_response_for_collection( $this->prepare_item_for_response ( array( |
169
|
|
|
'id' => $id, |
170
|
|
|
'name' => $tool['name'], |
171
|
|
|
'action' => $tool['button'], |
172
|
|
|
'description' => $tool['desc'], |
173
|
|
|
), $request ) ); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
$response = rest_ensure_response( $tools ); |
177
|
|
|
return $response; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Return a single tool. |
182
|
|
|
* |
183
|
|
|
* @param WP_REST_Request $request |
184
|
|
|
* @return WP_Error|WP_REST_Response |
185
|
|
|
*/ |
186
|
|
|
public function get_item( $request ) { |
187
|
|
|
$tools = $this->get_tools(); |
188
|
|
|
if ( empty( $tools[ $request['id'] ] ) ) { |
189
|
|
|
return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) ); |
190
|
|
|
} |
191
|
|
|
$tool = $tools[ $request['id'] ]; |
192
|
|
|
return rest_ensure_response( $this->prepare_item_for_response ( array( |
193
|
|
|
'id' => $request['id'], |
194
|
|
|
'name' => $tool['name'], |
195
|
|
|
'action' => $tool['button'], |
196
|
|
|
'description' => $tool['desc'], |
197
|
|
|
), $request ) ); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* Update (execute) a tool. |
202
|
|
|
|
203
|
|
|
* @param WP_REST_Request $request |
204
|
|
|
* @return WP_Error|WP_REST_Response |
205
|
|
|
*/ |
206
|
|
|
public function update_item( $request ) { |
207
|
|
|
$tools = $this->get_tools(); |
208
|
|
|
if ( empty( $tools[ $request['id'] ] ) ) { |
209
|
|
|
return new WP_Error( 'woocommerce_rest_system_status_tool_invalid_id', __( 'Invalid tool ID.', 'woocommerce' ), array( 'status' => 404 ) ); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
$tool = $tools[ $request['id'] ]; |
213
|
|
|
$tool = array( |
214
|
|
|
'id' => $request['id'], |
215
|
|
|
'name' => $tool['name'], |
216
|
|
|
'action' => $tool['button'], |
217
|
|
|
'description' => $tool['desc'], |
218
|
|
|
); |
219
|
|
|
|
220
|
|
|
$execute_return = $this->execute_tool( $request['id'] ); |
221
|
|
|
$tool = array_merge( $tool, $execute_return ); |
222
|
|
|
|
223
|
|
|
$request->set_param( 'context', 'edit' ); |
224
|
|
|
$response = $this->prepare_item_for_response( $tool, $request ); |
225
|
|
|
return rest_ensure_response( $response ); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* Prepare a tool item for serialization. |
230
|
|
|
* |
231
|
|
|
* @param array $item Object. |
232
|
|
|
* @param WP_REST_Request $request Request object. |
233
|
|
|
* @return WP_REST_Response $response Response data. |
234
|
|
|
*/ |
235
|
|
View Code Duplication |
public function prepare_item_for_response( $item, $request ) { |
|
|
|
|
236
|
|
|
$context = empty( $request['context'] ) ? 'view' : $request['context']; |
237
|
|
|
$data = $this->add_additional_fields_to_object( $item, $request ); |
238
|
|
|
$data = $this->filter_response_by_context( $data, $context ); |
239
|
|
|
|
240
|
|
|
$response = rest_ensure_response( $data ); |
241
|
|
|
|
242
|
|
|
$response->add_links( $this->prepare_links( $item['id'] ) ); |
243
|
|
|
|
244
|
|
|
return $response; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Get the system status tools schema, conforming to JSON Schema. |
249
|
|
|
* |
250
|
|
|
* @return array |
251
|
|
|
*/ |
252
|
|
|
public function get_item_schema() { |
253
|
|
|
$schema = array( |
254
|
|
|
'$schema' => 'http://json-schema.org/draft-04/schema#', |
255
|
|
|
'title' => 'system_status_tool', |
256
|
|
|
'type' => 'object', |
257
|
|
|
'properties' => array( |
258
|
|
|
'id' => array( |
259
|
|
|
'description' => __( 'A unique identifier for the tool.', 'woocommerce' ), |
260
|
|
|
'type' => 'string', |
261
|
|
|
'context' => array( 'view', 'edit' ), |
262
|
|
|
'arg_options' => array( |
263
|
|
|
'sanitize_callback' => 'sanitize_title', |
264
|
|
|
), |
265
|
|
|
), |
266
|
|
|
'name' => array( |
267
|
|
|
'description' => __( 'Tool name.', 'woocommerce' ), |
268
|
|
|
'type' => 'string', |
269
|
|
|
'context' => array( 'view', 'edit' ), |
270
|
|
|
'arg_options' => array( |
271
|
|
|
'sanitize_callback' => 'sanitize_text_field', |
272
|
|
|
), |
273
|
|
|
), |
274
|
|
|
'action' => array( |
275
|
|
|
'description' => __( 'What running the tool will do.', 'woocommerce' ), |
276
|
|
|
'type' => 'string', |
277
|
|
|
'context' => array( 'view', 'edit' ), |
278
|
|
|
'arg_options' => array( |
279
|
|
|
'sanitize_callback' => 'sanitize_text_field', |
280
|
|
|
), |
281
|
|
|
), |
282
|
|
|
'description' => array( |
283
|
|
|
'description' => __( 'Tool description.', 'woocommerce' ), |
284
|
|
|
'type' => 'string', |
285
|
|
|
'context' => array( 'view', 'edit' ), |
286
|
|
|
'arg_options' => array( |
287
|
|
|
'sanitize_callback' => 'sanitize_text_field', |
288
|
|
|
), |
289
|
|
|
), |
290
|
|
|
'success' => array( |
291
|
|
|
'description' => __( 'Did the tool run successfully?', 'woocommerce' ), |
292
|
|
|
'type' => 'boolean', |
293
|
|
|
'context' => array( 'edit' ), |
294
|
|
|
), |
295
|
|
|
'message' => array( |
296
|
|
|
'description' => __( 'Tool return message.', 'woocommerce' ), |
297
|
|
|
'type' => 'string', |
298
|
|
|
'context' => array( 'edit' ), |
299
|
|
|
'arg_options' => array( |
300
|
|
|
'sanitize_callback' => 'sanitize_text_field', |
301
|
|
|
), |
302
|
|
|
), |
303
|
|
|
), |
304
|
|
|
); |
305
|
|
|
|
306
|
|
|
return $this->add_additional_fields_schema( $schema ); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Prepare links for the request. |
311
|
|
|
* |
312
|
|
|
* @param string $id |
313
|
|
|
* @return array |
314
|
|
|
*/ |
315
|
|
View Code Duplication |
protected function prepare_links( $id ) { |
|
|
|
|
316
|
|
|
$base = '/' . $this->namespace . '/' . $this->rest_base; |
317
|
|
|
$links = array( |
318
|
|
|
'item' => array( |
319
|
|
|
'href' => rest_url( trailingslashit( $base ) . $id ), |
320
|
|
|
'embeddable' => true, |
321
|
|
|
), |
322
|
|
|
); |
323
|
|
|
|
324
|
|
|
return $links; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Get any query params needed. |
329
|
|
|
* |
330
|
|
|
* @return array |
331
|
|
|
*/ |
332
|
|
|
public function get_collection_params() { |
333
|
|
|
return array( |
334
|
|
|
'context' => $this->get_context_param( array( 'default' => 'view' ) ), |
335
|
|
|
); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Actually executes a a tool. |
340
|
|
|
* |
341
|
|
|
* @param string $tool |
342
|
|
|
* @return array |
343
|
|
|
*/ |
344
|
|
|
public function execute_tool( $tool ) { |
345
|
|
|
global $wpdb; |
346
|
|
|
$ran = true; |
347
|
|
|
switch ( $tool ) { |
348
|
|
|
case 'clear_transients' : |
349
|
|
|
wc_delete_product_transients(); |
350
|
|
|
wc_delete_shop_order_transients(); |
351
|
|
|
WC_Cache_Helper::get_transient_version( 'shipping', true ); |
352
|
|
|
$message = __( 'Product Transients Cleared', 'woocommerce' ); |
353
|
|
|
break; |
354
|
|
|
case 'clear_expired_transients' : |
355
|
|
|
/* |
356
|
|
|
* Deletes all expired transients. The multi-table delete syntax is used. |
357
|
|
|
* to delete the transient record from table a, and the corresponding. |
358
|
|
|
* transient_timeout record from table b. |
359
|
|
|
* |
360
|
|
|
* Based on code inside core's upgrade_network() function. |
361
|
|
|
*/ |
362
|
|
|
$sql = "DELETE a, b FROM $wpdb->options a, $wpdb->options b |
363
|
|
|
WHERE a.option_name LIKE %s |
364
|
|
|
AND a.option_name NOT LIKE %s |
365
|
|
|
AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) ) |
366
|
|
|
AND b.option_value < %d"; |
367
|
|
|
$rows = $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_transient_' ) . '%', $wpdb->esc_like( '_transient_timeout_' ) . '%', time() ) ); |
368
|
|
|
|
369
|
|
|
$sql = "DELETE a, b FROM $wpdb->options a, $wpdb->options b |
370
|
|
|
WHERE a.option_name LIKE %s |
371
|
|
|
AND a.option_name NOT LIKE %s |
372
|
|
|
AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) ) |
373
|
|
|
AND b.option_value < %d"; |
374
|
|
|
$rows2 = $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( '_site_transient_' ) . '%', $wpdb->esc_like( '_site_transient_timeout_' ) . '%', time() ) ); |
375
|
|
|
|
376
|
|
|
$message = sprintf( __( '%d Transients Rows Cleared', 'woocommerce' ), $rows + $rows2 ); |
377
|
|
|
break; |
378
|
|
|
case 'reset_roles' : |
379
|
|
|
// Remove then re-add caps and roles |
380
|
|
|
WC_Install::remove_roles(); |
381
|
|
|
WC_Install::create_roles(); |
382
|
|
|
$message = __( 'Roles successfully reset', 'woocommerce' ); |
383
|
|
|
break; |
384
|
|
|
case 'recount_terms' : |
385
|
|
|
$product_cats = get_terms( 'product_cat', array( 'hide_empty' => false, 'fields' => 'id=>parent' ) ); |
386
|
|
|
_wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false ); |
387
|
|
|
$product_tags = get_terms( 'product_tag', array( 'hide_empty' => false, 'fields' => 'id=>parent' ) ); |
388
|
|
|
_wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), true, false ); |
389
|
|
|
$message = __( 'Terms successfully recounted', 'woocommerce' ); |
390
|
|
|
break; |
391
|
|
|
case 'clear_sessions' : |
392
|
|
|
$wpdb->query( "TRUNCATE {$wpdb->prefix}woocommerce_sessions" ); |
393
|
|
|
wp_cache_flush(); |
394
|
|
|
$message = __( 'Sessions successfully cleared', 'woocommerce' ); |
395
|
|
|
break; |
396
|
|
|
case 'install_pages' : |
397
|
|
|
WC_Install::create_pages(); |
398
|
|
|
return __( 'All missing WooCommerce pages was installed successfully.', 'woocommerce' ); |
399
|
|
|
break; |
|
|
|
|
400
|
|
|
case 'delete_taxes' : |
401
|
|
|
|
402
|
|
|
$wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rates;" ); |
403
|
|
|
$wpdb->query( "TRUNCATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations;" ); |
404
|
|
|
WC_Cache_Helper::incr_cache_prefix( 'taxes' ); |
405
|
|
|
$message = __( 'Tax rates successfully deleted', 'woocommerce' ); |
406
|
|
|
break; |
407
|
|
|
case 'reset_tracking' : |
408
|
|
|
delete_option( 'woocommerce_allow_tracking' ); |
409
|
|
|
WC_Admin_Notices::add_notice( 'tracking' ); |
410
|
|
|
$message = __( 'Usage tracking settings successfully reset.', 'woocommerce' ); |
411
|
|
|
break; |
412
|
|
|
default : |
413
|
|
|
$tools = $this->get_tools(); |
414
|
|
|
if ( isset( $tools[ $tool ]['callback'] ) ) { |
415
|
|
|
$callback = $tools[ $tool ]['callback']; |
416
|
|
|
$return = call_user_func( $callback ); |
417
|
|
|
if ( $return === false ) { |
418
|
|
|
$callback_string = is_array( $callback ) ? get_class( $callback[0] ) . '::' . $callback[1] : $callback; |
419
|
|
|
$ran = false; |
420
|
|
|
$message = sprintf( __( 'There was an error calling %s', 'woocommerce' ), $callback_string ); |
421
|
|
|
} else { |
422
|
|
|
$message = __( 'Tool ran.', 'woocommerce' ); |
423
|
|
|
} |
424
|
|
|
} else { |
425
|
|
|
$ran = false; |
426
|
|
|
$message = __( 'There was an error calling this tool. There is no callback present.', 'woocommerce' ); |
427
|
|
|
} |
428
|
|
|
break; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
return array( 'success' => $ran, 'message' => $message ); |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
} |
435
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.