Completed
Push — master ( dbdb62...3e05fb )
by
unknown
13:15
created

WC_REST_System_Status_Tools_Controller::get_item()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 11
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 13
rs 9.4285
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
	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 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
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;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
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