PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.1
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.1
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
mainwp / modules / cost-tracker / rest-api / version2 / class-mainwp-rest-costs-controller.php

class-mainwp-rest-costs-controller.php in MainWP Dashboard: Self-hosted WordPress Management for Agencies 6.2.1, at modules/cost-tracker/rest-api/version2/class-mainwp-rest-costs-controller.php

1,009 lines 39.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * MainWP REST Controller
4 *
5 * This class handles the REST API
6 *
7 * @package MainWP\Dashboard
8 */
9
10 use MainWP\Dashboard\MainWP_DB;
11 use MainWP\Dashboard\MainWP_Exception;
12 use MainWP\Dashboard\MainWP_Extra_Exception;
13 use MainWP\Dashboard\MainWP_DB_Client;
14
15 use MainWP\Dashboard\Module\CostTracker\Cost_Tracker_DB;
16 use MainWP\Dashboard\Module\CostTracker\Cost_Tracker_Rest_Api_Handle_V1;
17 use MainWP\Dashboard\Module\CostTracker\Cost_Tracker_Admin;
18 use MainWP\Dashboard\Module\CostTracker\Cost_Tracker_Add_Edit;
19
20 // Exit if accessed directly.
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit;
23 }
24
25 /**
26 * Class MainWP_Rest_Costs_Controller
27 *
28 * @package MainWP\Dashboard
29 */
30 class MainWP_Rest_Costs_Controller extends MainWP_REST_Controller { //phpcs:ignore -- NOSONAR - multi methods.
31
32 /**
33 * Protected static variable to hold the single instance of the class.
34 *
35 * @var mixed Default null
36 */
37 private static $instance = null;
38
39
40 /**
41 * Endpoint namespace.
42 *
43 * @var string
44 */
45 protected $namespace = 'mainwp/v2';
46
47 /**
48 * Route base.
49 *
50 * @var string
51 */
52 protected $rest_base = 'costs';
53
54
55 /**
56 * Method instance()
57 *
58 * Create public static instance.
59 *
60 * @static
61 * @return static::$instance
62 */
63 public static function instance() {
64 if ( null === static::$instance ) {
65 static::$instance = new self();
66 }
67 return static::$instance;
68 }
69
70 /**
71 * Method register_routes()
72 *
73 * Creates the necessary endpoints for the api.
74 * Note, for a request to be successful the URL query parameters consumer_key and consumer_secret need to be set and correct.
75 */
76 public function register_routes() { // phpcs:ignore -- NOSONAR - complex.
77
78 // Retrieves all clients.
79 register_rest_route(
80 $this->namespace,
81 '/' . $this->rest_base,
82 array(
83 array(
84 'methods' => WP_REST_Server::READABLE,
85 'callback' => array( $this, 'get_items' ),
86 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
87 ),
88 )
89 );
90
91 // Adds new cost.
92 register_rest_route(
93 $this->namespace,
94 '/' . $this->rest_base . '/add',
95 array(
96 array(
97 'methods' => WP_REST_Server::EDITABLE,
98 'callback' => array( $this, 'create_item' ),
99 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
100 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
101 ),
102 )
103 );
104
105 // Retrieves cost Object by cost ID.
106 register_rest_route(
107 $this->namespace,
108 '/' . $this->rest_base . '/(?P<id>[\d]+)',
109 array(
110 array(
111 'methods' => WP_REST_Server::READABLE,
112 'callback' => array( $this, 'get_item' ),
113 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
114 ),
115 )
116 );
117
118 // Edits cost by cost ID.
119 register_rest_route(
120 $this->namespace,
121 '/' . $this->rest_base . '/(?P<id>[\d]+)/edit',
122 array(
123 array(
124 'methods' => WP_REST_Server::EDITABLE,
125 'callback' => array( $this, 'update_item' ),
126 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
127 'args' => $this->get_edit_cost_args(),
128 ),
129 )
130 );
131
132 // Removes cost by cost ID.
133 register_rest_route(
134 $this->namespace,
135 '/' . $this->rest_base . '/(?P<id>[\d]+)/remove',
136 array(
137 array(
138 'methods' => WP_REST_Server::DELETABLE,
139 'callback' => array( $this, 'delete_item' ),
140 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
141 ),
142 )
143 );
144
145 // Retrieves all sites for a cost by cost ID.
146 register_rest_route(
147 $this->namespace,
148 '/' . $this->rest_base . '/(?P<id>[\d]+)/sites',
149 array(
150 array(
151 'methods' => WP_REST_Server::READABLE,
152 'callback' => array( $this, 'get_sites_by_cost' ),
153 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
154 ),
155 )
156 );
157
158 // Retrieves all costs for a cost by cost ID.
159 register_rest_route(
160 $this->namespace,
161 '/' . $this->rest_base . '/(?P<id>[\d]+)/clients',
162 array(
163 array(
164 'methods' => WP_REST_Server::READABLE,
165 'callback' => array( $this, 'get_clients_by_cost' ),
166 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
167 ),
168 )
169 );
170
171 // Handle batch requests.
172 register_rest_route(
173 $this->namespace,
174 '/' . $this->rest_base . '/batch',
175 array(
176 array(
177 'methods' => WP_REST_Server::EDITABLE,
178 'callback' => array( $this, 'batch_items' ),
179 'permission_callback' => array( $this, 'get_rest_permissions_check' ),
180 ),
181 )
182 );
183 }
184
185 /**
186 * Prepare objects query.
187 *
188 * @since 5.2
189 * @param WP_REST_Request $request Full details about the request.
190 * @param string $type 'object'.
191 *
192 * @return array
193 */
194 protected function prepare_objects_query( $request, $type = 'object' ) {
195
196 // This is needed to get around an array to string notice in WC_REST_Orders_V2_Controller::prepare_objects_query.
197 $statuses = array();
198 if ( isset( $request['status'] ) ) {
199 $statuses = wp_parse_list( $request['status'] );
200 unset( $request['status'] );
201 }
202
203 $category = array();
204 if ( isset( $request['category'] ) ) {
205 $category = wp_parse_list( $request['category'] );
206 unset( $request['category'] );
207 }
208
209 $types = array();
210 if ( isset( $request['type'] ) ) {
211 $types = wp_parse_list( $request['type'] );
212 unset( $request['type'] );
213 }
214
215 $args = parent::prepare_objects_query( $request, $type );
216
217 $product_types = Cost_Tracker_Admin::get_product_types();
218
219 $payment_types = array(
220 'subscription' => esc_html__( 'Subscription', 'mainwp' ),
221 'lifetime' => esc_html__( 'Lifetime', 'mainwp' ),
222 );
223
224 $cost_status = Cost_Tracker_Admin::get_cost_status();
225
226 $args['status'] = array();
227 foreach ( $statuses as $status ) {
228 if ( isset( $cost_status[ $status ] ) ) {
229 $args['status'][] = $status;
230 }
231 }
232
233 // Put the statuses back for further processing (next/prev links, etc).
234 $request['status'] = $statuses;
235
236 $args['category'] = array();
237 foreach ( $category as $cat ) {
238 if ( isset( $product_types[ $cat ] ) || 'any' === $cat ) {
239 $args['category'][] = $cat;
240 }
241 }
242
243 // Put the statuses back for further processing (next/prev links, etc).
244 $request['category'] = $category;
245
246 $args['type'] = array();
247 foreach ( $types as $type ) {
248 if ( isset( $payment_types[ $type ] ) || 'any' === $type ) {
249 $args['type'][] = $type;
250 }
251 }
252
253 // Put the statuses back for further processing (next/prev links, etc).
254 $request['type'] = $types;
255
256 return $args;
257 }
258
259 /**
260 * Get all Clients.
261 *
262 * @param WP_REST_Request $request Full details about the request.
263 * @return WP_Error|WP_REST_Response
264 */
265 public function get_items( $request ) {
266
267 $valid_args = parent::prepare_objects_query( $request );
268 $valid_args = $this->validate_rest_args( $valid_args, $this->get_validate_args_params( 'get_costs' ) );
269
270 if ( is_wp_error( $valid_args ) ) {
271 return $valid_args;
272 }
273
274 $args = $this->prepare_objects_query( $request );
275
276 $costs = Cost_Tracker_DB::get_instance()->get_cost_tracker_by( 'all', null, $args );
277 $data = Cost_Tracker_Rest_Api_Handle_V1::instance()->prepare_api_costs_data( $costs );
278
279 $resp_data = array(
280 'success' => 1,
281 'total' => is_array( $data ) ? count( $data ) : 0,
282 'data' => $data,
283 );
284 return rest_ensure_response( $resp_data );
285 }
286
287
288 /**
289 * Create item.
290 *
291 * @param WP_REST_Request $request Full details about the request.
292 * @return WP_Error|WP_REST_Response
293 */
294 public function create_item( $request ) {
295 // handle_rest_update_insert_item() reads a non-empty id as "update that row", and
296 // the body bag outranks anything the create route registers, so the id is cleared
297 // out of every bag before the handler can point a create at an existing cost.
298 $request->set_param( 'id', null );
299 try {
300 $resp_data = $this->handle_rest_update_insert_item( $request );
301 } catch ( MainWP_Extra_Exception $e ) {
302 return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => 400 ) );
303 }
304 return rest_ensure_response( $resp_data );
305 }
306
307 /**
308 *
309 * Handles the saving item.
310 *
311 * @throws MainWP_Extra_Exception Exception happen.
312 * @param WP_REST_Request $request Full details about the request.
313 *
314 * @return mixed Save output.
315 */
316 public static function handle_rest_update_insert_item( $request ) { //phpcs:ignore -- NOSONAR - complex method.
317
318 //phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
319 $update = array();
320 $last_renewal = isset( $request['last_renewal'] ) ? strtotime( wp_unslash( $request['last_renewal'] ) ) : 0;
321 $update['name'] = sanitize_text_field( wp_unslash( $request['name'] ) );
322 $update['type'] = sanitize_text_field( wp_unslash( $request['payment_type'] ) );
323 $update['product_type'] = sanitize_text_field( wp_unslash( $request['product_type'] ) );
324 $update['slug'] = isset( $request['product_slug'] ) ? sanitize_text_field( wp_unslash( $request['product_slug'] ) ) : '';
325 $update['license_type'] = sanitize_text_field( wp_unslash( $request['license_type'] ) );
326 $update['cost_status'] = sanitize_text_field( wp_unslash( $request['cost_tracker_status'] ) );
327 $update['url'] = ! empty( $request['url'] ) ? esc_url_raw( wp_unslash( $request['url'] ) ) : '';
328 $update['cost_icon'] = ! empty( $request['icon_hidden'] ) ? sanitize_text_field( wp_unslash( $request['icon_hidden'] ) ) : '';
329 $update['cost_color'] = sanitize_hex_color( wp_unslash( $request['product_color'] ) );
330 $update['price'] = floatval( $request['price'] );
331 $update['payment_method'] = sanitize_text_field( wp_unslash( $request['payment_method'] ) );
332
333 $renewal_fequency = sanitize_text_field( wp_unslash( $request['renewal_type'] ) );
334 $update['renewal_type'] = $renewal_fequency;
335 $update['last_renewal'] = $last_renewal; // labeled Purchase date.
336
337 $next_renewal = Cost_Tracker_Admin::get_next_renewal( $last_renewal, $renewal_fequency );
338 $update['next_renewal'] = $next_renewal;
339
340 $note = isset( $request['note'] ) ? wp_unslash( $request['note'] ) : '';
341 $esc_note = apply_filters( 'mainwp_escape_content', $note );
342 $update['note'] = $esc_note;
343
344 $selected_sites = array();
345 $selected_groups = array();
346 $selected_clients = array();
347
348 // The admin form persists all three lists and every reader unions the three
349 // columns, so a chain here would silently drop the second selector a caller sent.
350 if ( isset( $request['sites'] ) && is_array( $request['sites'] ) ) {
351 foreach ( wp_unslash( $request['sites'] ) as $selected ) {
352 $selected_sites[] = intval( $selected );
353 }
354 }
355
356 if ( isset( $request['groups'] ) && is_array( $request['groups'] ) ) {
357 foreach ( wp_unslash( $request['groups'] ) as $selected ) {
358 $selected_groups[] = intval( $selected );
359 }
360 }
361
362 if ( isset( $request['clients'] ) && is_array( $request['clients'] ) ) {
363 foreach ( wp_unslash( $request['clients'] ) as $selected ) {
364 $selected_clients[] = intval( $selected );
365 }
366 }
367
368 $update['sites'] = ! empty( $selected_sites ) ? wp_json_encode( $selected_sites ) : '';
369 $update['groups'] = ! empty( $selected_groups ) ? wp_json_encode( $selected_groups ) : '';
370 $update['clients'] = ! empty( $selected_clients ) ? wp_json_encode( $selected_clients ) : '';
371
372 if ( empty( $update['sites'] ) && empty( $update['groups'] ) && empty( $update['clients'] ) ) {
373 throw new MainWP_Extra_Exception( 'mainwp_rest_create_costs_invalid_data', esc_html__( 'Please enter websites or groups or clients.', 'mainwp' ) );
374 }
375
376 $current = false;
377 if ( ! empty( $request['id'] ) ) {
378 $update['id'] = intval( $request['id'] );
379 $current = Cost_Tracker_DB::get_instance()->get_cost_tracker_by( 'id', $update['id'] );
380 }
381
382 //phpcs:enable
383 $err_msg = '';
384 $output = false;
385
386 if ( ! isset( $update['cost_color'] ) ) {
387 $update['cost_color'] = ''; // can not be null.
388 }
389
390 try {
391 $output = Cost_Tracker_DB::get_instance()->update_cost_tracker( $update );
392 if ( $output && ! empty( $output->id ) ) {
393 Cost_Tracker_DB::get_instance()->update_selected_lookup_cost( $output->id, $selected_sites, $selected_groups, $selected_clients );
394 if ( $current && ! empty( $current->cost_icon ) && false === strpos( $current->cost_icon, 'deficon:' ) && $current->cost_icon !== $update['cost_icon'] ) {
395 Cost_Tracker_Add_Edit::get_instance()->delete_product_icon_file( $current->cost_icon );
396 }
397 }
398 } catch ( MainWP_Exception $ex ) {
399 $err_msg = $ex->getMessage();
400 }
401
402 if ( empty( $err_msg ) && ! empty( $output ) ) { // success.
403 return array(
404 'success' => 1,
405 'data' => $output,
406 );
407 } elseif ( ! empty( $err_msg ) ) { // error.
408 return array(
409 'success' => 0,
410 'error' => $err_msg,
411 );
412 }
413 return array(
414 'success' => 0,
415 'error' => esc_html__( 'Add cost failed. Please try again.', 'mainwp' ),
416 );
417 }
418
419
420 /**
421 * Make the id in the path the id every parameter bag reports.
422 *
423 * WP_REST_Request ranks body and query params above URL params, so a body id would
424 * decide which cost a /costs/{id}/edit or /costs/{id}/remove call writes to, and a
425 * ?id= query param which cost the read routes return. The batch route carries no
426 * path id, so its items keep addressing themselves by the id in the item body.
427 * set_param() overwrites the key in every bag that already holds it.
428 *
429 * @param WP_REST_Request $request Full details about the request.
430 * @return void
431 */
432 protected function pin_id_to_path( $request ) {
433 $url_params = $request->get_url_params();
434 if ( isset( $url_params['id'] ) ) {
435 $request->set_param( 'id', (int) $url_params['id'] );
436 }
437 }
438
439 /**
440 * Get site by.
441 *
442 * @param WP_REST_Request $request Full details about the request.
443 * @return WP_Error|Object Item.
444 */
445 public function get_request_item( $request ) {
446 $this->pin_id_to_path( $request );
447 $id = $request['id'];
448 $item = Cost_Tracker_DB::get_instance()->get_cost_tracker_by( 'id', $id );
449 if ( empty( $item ) ) {
450 return $this->get_rest_data_error( 'id', 'costs' );
451 }
452 return $item;
453 }
454
455
456 /**
457 * Get cost.
458 *
459 * @param WP_REST_Request $request Full details about the request.
460 * @return WP_Error|WP_REST_Response
461 */
462 public function get_item( $request ) {
463 $cost = $this->get_request_item( $request );
464 if ( is_wp_error( $cost ) ) {
465 return $cost;
466 }
467 return rest_ensure_response(
468 array(
469 'success' => 1,
470 'data' => $cost,
471 )
472 );
473 }
474
475 /**
476 * Update item.
477 *
478 * @param WP_REST_Request $request Full details about the request.
479 * @return WP_Error|WP_REST_Response
480 */
481 public function update_item( $request ) {
482
483 $cost = $this->get_request_item( $request );
484 if ( is_wp_error( $cost ) ) {
485 return $cost;
486 }
487
488 try {
489 $resp_data = $this->handle_rest_update_insert_item( $request );
490 } catch ( MainWP_Extra_Exception $e ) {
491 return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => 400 ) );
492 }
493 return rest_ensure_response( $resp_data );
494 }
495
496
497 /**
498 * Delete item.
499 *
500 * @param WP_REST_Request $request Full details about the request.
501 * @return WP_Error|WP_REST_Response
502 */
503 public function delete_item( $request ) {
504
505 $cost = $this->get_request_item( $request );
506 if ( is_wp_error( $cost ) ) {
507 return $cost;
508 }
509
510 $deleted = Cost_Tracker_DB::get_instance()->delete_cost_tracker( 'id', $cost->id );
511
512 return rest_ensure_response(
513 array(
514 'success' => $deleted ? 1 : 0,
515 'message' => $deleted
516 ? esc_html__( 'Cost deleted successfully.', 'mainwp' )
517 : esc_html__( 'Failed to delete cost.', 'mainwp' ),
518 'data' => $cost,
519 )
520 );
521 }
522
523
524 /**
525 * Get sites by item.
526 *
527 * @param WP_REST_Request $request Full details about the request.
528 * @return WP_Error|WP_REST_Response
529 */
530 public function get_sites_by_cost( $request ) {
531
532 $cost = $this->get_request_item( $request );
533
534 if ( is_wp_error( $cost ) ) {
535 return $cost;
536 }
537
538 $sites = ! empty( $cost->sites ) ? json_decode( $cost->sites, true ) : array();
539 $groups = ! empty( $cost->groups ) ? json_decode( $cost->groups, true ) : array();
540 $clients = ! empty( $cost->clients ) ? json_decode( $cost->clients, true ) : array();
541
542 if ( ! is_array( $sites ) ) {
543 $sites = array();
544 }
545 if ( ! is_array( $groups ) ) {
546 $groups = array();
547 }
548 if ( ! is_array( $clients ) ) {
549 $clients = array();
550 }
551
552 $data = array();
553
554 if ( ! empty( $sites ) || ! empty( $groups ) || ! empty( $clients ) ) {
555 $params = array(
556 'sites' => $sites,
557 'groups' => $groups,
558 'clients' => $clients,
559 'selectgroups' => true,
560 'schema_fields' => apply_filters( 'mainwp_rest_routes_sites_controller_get_allowed_fields_by_context', 'view' ),
561 );
562 // to do: should create relation of: costs, sites, groups, clients in other way.
563 // to make it more easier in queries.
564 $cost_sites = MainWP_DB::instance()->get_db_sites( $params );
565 if ( is_array( $cost_sites ) ) {
566 foreach ( $cost_sites as $site ) {
567 $data[] = apply_filters( 'mainwp_rest_routes_sites_controller_filter_allowed_fields_by_context', $site );
568 }
569 }
570 }
571
572 return rest_ensure_response(
573 array(
574 'success' => 1,
575 'total' => count( $data ),
576 'data' => $data,
577 )
578 );
579 }
580
581 /**
582 * Get clients by item.
583 *
584 * @param WP_REST_Request $request Full details about the request.
585 * @return WP_Error|WP_REST_Response
586 */
587 public function get_clients_by_cost( $request ) { //phpcs:ignore -- NOSONAR - complex.
588
589 $cost = $this->get_request_item( $request );
590
591 if ( is_wp_error( $cost ) ) {
592 return $cost;
593 }
594
595 $sites = ! empty( $cost->sites ) ? json_decode( $cost->sites, true ) : array();
596 $groups = ! empty( $cost->groups ) ? json_decode( $cost->groups, true ) : array();
597 $clients = ! empty( $cost->clients ) ? json_decode( $cost->clients, true ) : array();
598
599 if ( ! is_array( $sites ) ) {
600 $sites = array();
601 }
602 if ( ! is_array( $groups ) ) {
603 $groups = array();
604 }
605 if ( ! is_array( $clients ) ) {
606 $clients = array();
607 }
608
609 $data = array();
610
611 if ( ! empty( $sites ) || ! empty( $groups ) || ! empty( $clients ) ) {
612 $params = array(
613 'sites' => $sites,
614 'groups' => $groups,
615 'clients' => $clients,
616 );
617
618 // to do: should create relation of: costs, sites, groups, clients in other way.
619 // to make it more easier in queries.
620 $cost_sites = MainWP_DB::instance()->get_db_sites( $params );
621
622 $client_ids = array();
623 if ( is_array( $cost_sites ) ) {
624 foreach ( $cost_sites as $site ) {
625 if ( ! in_array( $site->client_id, $client_ids ) ) {
626 $client_ids[] = $site->client_id;
627 }
628 }
629 }
630
631 if ( ! empty( $client_ids ) ) {
632 $prepared_args = array(
633 'include' => $client_ids,
634 );
635 // get data.
636 $clients = MainWP_DB_Client::instance()->get_wp_clients( $prepared_args );
637 if ( ! empty( $clients ) ) {
638 foreach ( $clients as $client ) {
639 $data[] = apply_filters( 'mainwp_rest_routes_clients_controller_filter_allowed_fields_by_context', $client );
640 }
641 }
642 }
643 }
644
645 return rest_ensure_response(
646 array(
647 'success' => 1,
648 'total' => count( $data ),
649 'data' => $data,
650 )
651 );
652 }
653
654 /**
655 * Get the query params for collections.
656 *
657 * @return array
658 */
659 public function get_collection_params() {
660
661 $params = parent::get_collection_params();
662
663 $params['context']['default'] = 'view';
664
665 $params['after'] = array(
666 'description' => __( 'Limit response to resources published after a given ISO8601 compliant date.', 'mainwp' ),
667 'type' => 'string',
668 'format' => 'date-time',
669 );
670 $params['before'] = array(
671 'description' => __( 'Limit response to reviews published before a given ISO8601 compliant date.', 'mainwp' ),
672 'type' => 'string',
673 'format' => 'date-time',
674 );
675 $params['exclude'] = array(
676 'description' => __( 'Ensure result set excludes specific IDs.', 'mainwp' ),
677 'type' => 'array',
678 'items' => array(
679 'type' => 'integer',
680 ),
681 'default' => array(),
682 );
683 $params['include'] = array(
684 'description' => __( 'Limit result set to specific IDs.', 'mainwp' ),
685 'type' => 'array',
686 'items' => array(
687 'type' => 'integer',
688 ),
689 'default' => array(),
690 );
691 $params['offset'] = array(
692 'description' => __( 'Offset the result set by a specific number of items.', 'mainwp' ),
693 'type' => 'integer',
694 );
695 $params['order'] = array(
696 'description' => __( 'Order sort attribute ascending or descending.', 'mainwp' ),
697 'type' => 'string',
698 'default' => 'desc',
699 'enum' => array(
700 'asc',
701 'desc',
702 ),
703 );
704 $params['orderby'] = array(
705 'description' => __( 'Sort collection by object attribute.', 'mainwp' ),
706 'type' => 'string',
707 'default' => 'date_gmt',
708 'enum' => array(
709 'date',
710 'date_gmt',
711 'id',
712 'include',
713 'product',
714 ),
715 );
716 $params['reviewer'] = array(
717 'description' => __( 'Limit result set to reviews assigned to specific user IDs.', 'mainwp' ),
718 'type' => 'array',
719 'items' => array(
720 'type' => 'integer',
721 ),
722 );
723 $params['reviewer_exclude'] = array(
724 'description' => __( 'Ensure result set excludes reviews assigned to specific user IDs.', 'mainwp' ),
725 'type' => 'array',
726 'items' => array(
727 'type' => 'integer',
728 ),
729 );
730 $params['reviewer_email'] = array(
731 'default' => null,
732 'description' => __( 'Limit result set to that from a specific author email.', 'mainwp' ),
733 'format' => 'email',
734 'type' => 'string',
735 );
736 $params['product'] = array(
737 'default' => array(),
738 'description' => __( 'Limit result set to reviews assigned to specific product IDs.', 'mainwp' ),
739 'type' => 'array',
740 'items' => array(
741 'type' => 'integer',
742 ),
743 );
744 $params['status'] = array(
745 'default' => 'approved',
746 'description' => __( 'Limit result set to reviews assigned a specific status.', 'mainwp' ),
747 'sanitize_callback' => 'sanitize_key',
748 'type' => 'string',
749 'enum' => array(
750 'all',
751 'hold',
752 'approved',
753 'spam',
754 'trash',
755 ),
756 );
757
758 /**
759 * Filter collection parameters.
760 *
761 * This filter registers the collection parameter, but does not map the
762 * collection parameter to an internal WP_Comment_Query parameter. Use the
763 * `wc_rest_review_query` filter to set WP_Comment_Query parameters.
764 *
765 * @since 5.2
766 * @param array $params JSON Schema-formatted collection parameters.
767 */
768 return apply_filters( 'mainwp_rest_cost_collection_params', $params );
769 }
770
771
772 /**
773 * Arguments registered by the edit route.
774 *
775 * The edit route identifies the cost by its URL segment, so the schema's id arg is
776 * dropped here rather than letting a body id point the update somewhere else. Schema
777 * defaults are dropped too: core injects arg defaults as request params, and
778 * handle_rest_update_insert_item() writes every field it reads, so a defaulted arg
779 * would overwrite a stored value the caller never sent.
780 *
781 * @return array
782 */
783 protected function get_edit_cost_args() {
784 $args = $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE );
785 unset( $args['id'] );
786 foreach ( $args as &$arg ) {
787 unset( $arg['default'] );
788 }
789 unset( $arg );
790 return $args;
791 }
792
793 /**
794 * Get the costs schema, conforming to JSON Schema.
795 *
796 * The properties are named the way the wire names them, which is what
797 * handle_rest_update_insert_item() reads and what the docs document. Five of them
798 * used to carry their DB column name instead, so the args derived from this schema
799 * validated names no write path ever looked at.
800 *
801 * @since 5.2
802 * @return array
803 */
804 public function get_item_schema() {
805 return array(
806 '$schema' => 'http://json-schema.org/draft-04/schema#',
807 'title' => 'costs',
808 'type' => 'object',
809 'properties' => array(
810 'id' => array(
811 'type' => 'integer',
812 'description' => __( 'Cost ID.', 'mainwp' ),
813 'sanitize_callback' => 'absint',
814 'validate_callback' => 'rest_validate_request_arg',
815 'context' => array( 'view', 'edit' ),
816 // The write routes take the cost from the path, and batch items from the
817 // id arg get_batch_update_args() adds, so no route accepts it as input.
818 'readonly' => true,
819 ),
820 'name' => array(
821 'type' => 'string',
822 'description' => __( 'Cost name.', 'mainwp' ),
823 'sanitize_callback' => 'sanitize_text_field',
824 'validate_callback' => 'rest_validate_request_arg',
825 'context' => array( 'view', 'edit' ),
826 ),
827 'url' => array(
828 'type' => 'string',
829 'description' => __( 'Cost url.', 'mainwp' ),
830 'sanitize_callback' => 'sanitize_url',
831 'validate_callback' => 'rest_validate_request_arg',
832 'context' => array( 'view', 'edit' ),
833 ),
834 'payment_type' => array(
835 'type' => 'string',
836 'description' => __( 'Payment type.', 'mainwp' ),
837 'sanitize_callback' => 'sanitize_text_field',
838 'validate_callback' => 'rest_validate_request_arg',
839 'context' => array( 'view', 'edit' ),
840 ),
841 'product_type' => array(
842 'type' => 'string',
843 'description' => __( 'Product type.', 'mainwp' ),
844 'sanitize_callback' => 'sanitize_text_field',
845 'validate_callback' => 'rest_validate_request_arg',
846 'context' => array( 'view', 'edit' ),
847 ),
848 'product_slug' => array(
849 'type' => 'string',
850 'description' => __( 'Product slug.', 'mainwp' ),
851 'sanitize_callback' => 'sanitize_text_field',
852 'validate_callback' => 'rest_validate_request_arg',
853 'context' => array( 'view', 'edit' ),
854 ),
855 'license_type' => array(
856 'type' => 'string',
857 'description' => __( 'License type.', 'mainwp' ),
858 'sanitize_callback' => 'sanitize_text_field',
859 'validate_callback' => 'rest_validate_request_arg',
860 'context' => array( 'view', 'edit' ),
861 ),
862 'cost_tracker_status' => array(
863 'type' => 'string',
864 'description' => __( 'Cost status.', 'mainwp' ),
865 'sanitize_callback' => 'sanitize_text_field',
866 'validate_callback' => 'rest_validate_request_arg',
867 'context' => array( 'view', 'edit' ),
868 ),
869 'payment_method' => array(
870 'type' => 'string',
871 'description' => __( 'Payment method.', 'mainwp' ),
872 'sanitize_callback' => 'sanitize_text_field',
873 'validate_callback' => 'rest_validate_request_arg',
874 'context' => array( 'view', 'edit' ),
875 ),
876 'price' => array(
877 'type' => 'number',
878 'description' => __( 'Price.', 'mainwp' ),
879 'sanitize_callback' => 'sanitize_text_field',
880 'validate_callback' => 'rest_validate_request_arg',
881 'context' => array( 'view', 'edit' ),
882 ),
883 'renewal_type' => array(
884 'type' => 'string',
885 'description' => __( 'Renewal type.', 'mainwp' ),
886 'sanitize_callback' => 'sanitize_text_field',
887 'validate_callback' => 'rest_validate_request_arg',
888 'context' => array( 'view', 'edit' ),
889 ),
890 // Read with strtotime(), and documented as any format it accepts, so an
891 // integer arg would reject every date string the write routes are given.
892 'last_renewal' => array(
893 'type' => 'string',
894 'description' => __( 'Last renewal date, in any format strtotime() accepts.', 'mainwp' ),
895 'sanitize_callback' => 'sanitize_text_field',
896 'validate_callback' => 'rest_validate_request_arg',
897 'context' => array( 'view', 'edit' ),
898 ),
899 'next_renewal' => array(
900 'type' => 'integer',
901 'description' => __( 'Next renewal.', 'mainwp' ),
902 'sanitize_callback' => 'absint',
903 'validate_callback' => 'rest_validate_request_arg',
904 'context' => array( 'view', 'edit' ),
905 // Derived by the handler and rendered as a date string on read, so a
906 // read-then-write round trip must not be validated against the integer.
907 'readonly' => true,
908 ),
909 'last_alert' => array(
910 'type' => 'integer',
911 'description' => __( 'Last alert.', 'mainwp' ),
912 'sanitize_callback' => 'absint',
913 'validate_callback' => 'rest_validate_request_arg',
914 'context' => array( 'view', 'edit' ),
915 // Derived by the handler and rendered as a date string on read, so a
916 // read-then-write round trip must not be validated against the integer.
917 'readonly' => true,
918 ),
919 'icon_hidden' => array(
920 'type' => 'string',
921 'description' => __( 'Cost icon.', 'mainwp' ),
922 'sanitize_callback' => 'sanitize_text_field',
923 'validate_callback' => 'rest_validate_request_arg',
924 'context' => array( 'view', 'edit' ),
925 ),
926 'product_color' => array(
927 'type' => 'string',
928 'description' => __( 'Cost color.', 'mainwp' ),
929 'sanitize_callback' => 'sanitize_text_field',
930 'validate_callback' => 'rest_validate_request_arg',
931 'context' => array( 'view', 'edit' ),
932 ),
933 // handle_rest_update_insert_item() requires one of these three to be an
934 // array of ids, so typing them as strings made every validated create fail.
935 'sites' => array(
936 'type' => 'array',
937 'description' => __( 'Sites IDs.', 'mainwp' ),
938 'items' => array(
939 'type' => 'integer',
940 ),
941 'sanitize_callback' => 'wp_parse_id_list',
942 'validate_callback' => 'rest_validate_request_arg',
943 'context' => array( 'view', 'edit' ),
944 ),
945 'groups' => array(
946 'type' => 'array',
947 'description' => __( 'Groups IDs.', 'mainwp' ),
948 'items' => array(
949 'type' => 'integer',
950 ),
951 'sanitize_callback' => 'wp_parse_id_list',
952 'validate_callback' => 'rest_validate_request_arg',
953 'context' => array( 'view', 'edit' ),
954 ),
955 'clients' => array(
956 'type' => 'array',
957 'description' => __( 'Clients IDs.', 'mainwp' ),
958 'items' => array(
959 'type' => 'integer',
960 ),
961 'sanitize_callback' => 'wp_parse_id_list',
962 'validate_callback' => 'rest_validate_request_arg',
963 'context' => array( 'view', 'edit' ),
964 ),
965 'note' => array(
966 'type' => 'string',
967 'description' => __( 'Note.', 'mainwp' ),
968 'sanitize_callback' => 'sanitize_text_field',
969 'validate_callback' => 'rest_validate_request_arg',
970 'context' => array( 'view', 'edit' ),
971 ),
972 ),
973 );
974 }
975
976 /**
977 * Prepare a single product review output for response.
978 *
979 * @param WP_Comment $review Product review object.
980 * @param WP_REST_Request $request Request object.
981 * @return WP_REST_Response $response Response data.
982 */
983 public function prepare_item_for_response( $review, $request ) {
984 $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
985 $fields = $this->get_fields_for_response( $request );
986 $data = array();
987
988 if ( in_array( 'id', $fields, true ) ) {
989 $data['id'] = (int) $review->comment_ID;
990 }
991 if ( in_array( 'date_created', $fields, true ) ) {
992 $data['date_created'] = mainwp_rest_prepare_date_response( $review->comment_date );
993 }
994 if ( in_array( 'date_created_gmt', $fields, true ) ) {
995 $data['date_created_gmt'] = mainwp_rest_prepare_date_response( $review->comment_date_gmt );
996 }
997
998 $data = $this->filter_response_by_context( $data, $context );
999
1000 /**
1001 * Filter product reviews object returned from the REST API.
1002 *
1003 * @param array $data The object.
1004 * @param WP_REST_Request $request Request object.
1005 */
1006 return apply_filters( 'mainwp_rest_prepare_cost', $data, $review, $request );
1007 }
1008 }
1009