1 <?php
2 3 4 5 6 7 8 9 10 11
12
13 if ( ! defined( 'ABSPATH' ) ) exit;
14
15 class WC_API_Resource {
16
17
18 protected $server;
19
20
21 protected $base;
22
23 24 25 26 27 28 29
30 public function __construct( WC_API_Server $server ) {
31
32 $this->server = $server;
33
34
35 add_filter( 'woocommerce_api_endpoints', array( $this, 'register_routes' ) );
36
37
38
39
40 foreach ( array( 'order', 'coupon', 'customer', 'product', 'report' ) as $resource ) {
41
42 add_filter( "woocommerce_api_{$resource}_response", array( $this, 'maybe_add_meta' ), 15, 2 );
43 add_filter( "woocommerce_api_{$resource}_response", array( $this, 'filter_response_fields' ), 20, 3 );
44 }
45 }
46
47 48 49 50 51 52 53 54 55 56 57 58 59
60 protected function validate_request( $id, $type, $context ) {
61
62 if ( 'shop_order' === $type || 'shop_coupon' === $type )
63 $resource_name = str_replace( 'shop_', '', $type );
64 else
65 $resource_name = $type;
66
67 $id = absint( $id );
68
69
70 if ( empty( $id ) )
71 return new WP_Error( "woocommerce_api_invalid_{$resource_name}_id", sprintf( __( 'Invalid %s ID', 'woocommerce' ), $type ), array( 'status' => 404 ) );
72
73
74 if ( 'customer' !== $type ) {
75
76 $post = get_post( $id );
77
78
79 $post_type = ( 'product_variation' === $post->post_type ) ? 'product' : $post->post_type;
80
81
82 if ( $type !== $post_type )
83 return new WP_Error( "woocommerce_api_invalid_{$resource_name}", sprintf( __( 'Invalid %s', 'woocommerce' ), $resource_name ), array( 'status' => 404 ) );
84
85
86 switch ( $context ) {
87
88 case 'read':
89 if ( ! $this->is_readable( $post ) )
90 return new WP_Error( "woocommerce_api_user_cannot_read_{$resource_name}", sprintf( __( 'You do not have permission to read this %s', 'woocommerce' ), $resource_name ), array( 'status' => 401 ) );
91 break;
92
93 case 'edit':
94 if ( ! $this->is_editable( $post ) )
95 return new WP_Error( "woocommerce_api_user_cannot_edit_{$resource_name}", sprintf( __( 'You do not have permission to edit this %s', 'woocommerce' ), $resource_name ), array( 'status' => 401 ) );
96 break;
97
98 case 'delete':
99 if ( ! $this->is_deletable( $post ) )
100 return new WP_Error( "woocommerce_api_user_cannot_delete_{$resource_name}", sprintf( __( 'You do not have permission to delete this %s', 'woocommerce' ), $resource_name ), array( 'status' => 401 ) );
101 break;
102 }
103 }
104
105 return $id;
106 }
107
108 109 110 111 112 113 114 115
116 protected function merge_query_args( $base_args, $request_args ) {
117
118 $args = array();
119
120
121 if ( ! empty( $request_args['created_at_min'] ) || ! empty( $request_args['created_at_max'] ) || ! empty( $request_args['updated_at_min'] ) || ! empty( $request_args['updated_at_max'] ) ) {
122
123 $args['date_query'] = array();
124
125
126 if ( ! empty( $request_args['created_at_min'] ) )
127 $args['date_query'][] = array( 'column' => 'post_date_gmt', 'after' => $this->server->parse_datetime( $request_args['created_at_min'] ), 'inclusive' => true );
128
129
130 if ( ! empty( $request_args['created_at_max'] ) )
131 $args['date_query'][] = array( 'column' => 'post_date_gmt', 'before' => $this->server->parse_datetime( $request_args['created_at_max'] ), 'inclusive' => true );
132
133
134 if ( ! empty( $request_args['updated_at_min'] ) )
135 $args['date_query'][] = array( 'column' => 'post_modified_gmt', 'after' => $this->server->parse_datetime( $request_args['updated_at_min'] ), 'inclusive' => true );
136
137
138 if ( ! empty( $request_args['updated_at_max'] ) )
139 $args['date_query'][] = array( 'column' => 'post_modified_gmt', 'before' => $this->server->parse_datetime( $request_args['updated_at_max'] ), 'inclusive' => true );
140 }
141
142
143 if ( ! empty( $request_args['q'] ) )
144 $args['s'] = $request_args['q'];
145
146
147 if ( ! empty( $request_args['limit'] ) )
148 $args['posts_per_page'] = $request_args['limit'];
149
150
151 if ( ! empty( $request_args['offset'] ) )
152 $args['offset'] = $request_args['offset'];
153
154
155 $args['paged'] = ( isset( $request_args['page'] ) ) ? absint( $request_args['page'] ) : 1;
156
157 return array_merge( $base_args, $args );
158 }
159
160 161 162 163 164 165 166 167 168
169 public function maybe_add_meta( $data, $resource ) {
170
171 if ( isset( $this->server->params['GET']['filter']['meta'] ) && 'true' === $this->server->params['GET']['filter']['meta'] && is_object( $resource ) ) {
172
173
174 if ( preg_grep( '/[a-z]+_meta/', array_keys( $data ) ) )
175 return $data;
176
177
178 switch ( get_class( $resource ) ) {
179
180 case 'WC_Order':
181 $meta_name = 'order_meta';
182 break;
183
184 case 'WC_Coupon':
185 $meta_name = 'coupon_meta';
186 break;
187
188 case 'WP_User':
189 $meta_name = 'customer_meta';
190 break;
191
192 default:
193 $meta_name = 'product_meta';
194 break;
195 }
196
197 if ( is_a( $resource, 'WP_User' ) ) {
198
199
200 $meta = (array) get_user_meta( $resource->ID );
201
202 } elseif ( is_a( $resource, 'WC_Product_Variation' ) ) {
203
204
205 $meta = (array) get_post_meta( $resource->get_variation_id() );
206
207 } else {
208
209
210 $meta = (array) get_post_meta( $resource->id );
211 }
212
213 foreach( $meta as $meta_key => $meta_value ) {
214
215
216 if ( ! is_protected_meta( $meta_key ) ) {
217 $data[ $meta_name ][ $meta_key ] = maybe_unserialize( $meta_value[0] );
218 }
219 }
220
221 }
222
223 return $data;
224 }
225
226 227 228 229 230 231 232 233 234
235 public function filter_response_fields( $data, $resource, $fields ) {
236
237 if ( ! is_array( $data ) || empty( $fields ) )
238 return $data;
239
240 $fields = explode( ',', $fields );
241 $sub_fields = array();
242
243
244 foreach ( $fields as $field ) {
245
246 if ( false !== strpos( $field, '.' ) ) {
247
248 list( $name, $value ) = explode( '.', $field );
249
250 $sub_fields[ $name ] = $value;
251 }
252 }
253
254
255 foreach ( $data as $data_field => $data_value ) {
256
257
258 if ( is_array( $data_value ) && in_array( $data_field, array_keys( $sub_fields ) ) ) {
259
260
261 foreach ( $data_value as $sub_field => $sub_field_value ) {
262
263
264 if ( ! in_array( $sub_field, $sub_fields ) ) {
265 unset( $data[ $data_field ][ $sub_field ] );
266 }
267 }
268
269 } else {
270
271
272 if ( ! in_array( $data_field, $fields ) ) {
273 unset( $data[ $data_field ] );
274 }
275 }
276 }
277
278 return $data;
279 }
280
281 282 283 284 285 286 287 288 289
290 protected function delete( $id, $type, $force = false ) {
291
292 if ( 'shop_order' === $type || 'shop_coupon' === $type )
293 $resource_name = str_replace( 'shop_', '', $type );
294 else
295 $resource_name = $type;
296
297 if ( 'customer' === $type ) {
298
299 $result = wp_delete_user( $id );
300
301 if ( $result )
302 return array( 'message' => __( 'Permanently deleted customer', 'woocommerce' ) );
303 else
304 return new WP_Error( 'woocommerce_api_cannot_delete_customer', __( 'The customer cannot be deleted', 'woocommerce' ), array( 'status' => 500 ) );
305
306 } else {
307
308
309
310 $result = ( $force ) ? wp_delete_post( $id, true ) : wp_trash_post( $id );
311
312 if ( ! $result )
313 return new WP_Error( "woocommerce_api_cannot_delete_{$resource_name}", sprintf( __( 'This %s cannot be deleted', 'woocommerce' ), $resource_name ), array( 'status' => 500 ) );
314
315 if ( $force ) {
316 return array( 'message' => sprintf( __( 'Permanently deleted %s', 'woocommerce' ), $resource_name ) );
317
318 } else {
319
320 $this->server->send_status( '202' );
321
322 return array( 'message' => sprintf( __( 'Deleted %s', 'woocommerce' ), $resource_name ) );
323 }
324 }
325 }
326
327
328 329 330 331 332 333 334 335
336 protected function is_readable( $post ) {
337
338 return $this->check_permission( $post, 'read' );
339 }
340
341 342 343 344 345 346 347 348
349 protected function is_editable( $post ) {
350
351 return $this->check_permission( $post, 'edit' );
352
353 }
354
355 356 357 358 359 360 361 362
363 protected function is_deletable( $post ) {
364
365 return $this->check_permission( $post, 'delete' );
366 }
367
368 369 370 371 372 373 374 375
376 private function check_permission( $post, $context ) {
377
378 if ( ! is_a( $post, 'WP_Post' ) )
379 $post = get_post( $post );
380
381 if ( is_null( $post ) )
382 return false;
383
384 $post_type = get_post_type_object( $post->post_type );
385
386 if ( 'read' === $context )
387 return current_user_can( $post_type->cap->read_private_posts, $post->ID );
388
389 elseif ( 'edit' === $context )
390 return current_user_can( $post_type->cap->edit_post, $post->ID );
391
392 elseif ( 'delete' === $context )
393 return current_user_can( $post_type->cap->delete_post, $post->ID );
394
395 else
396 return false;
397 }
398
399 }
400