PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.7.3
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.7.3
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / jwt / rest-api / version1 / class-lp-rest-courses-v1-controller.php

class-lp-rest-courses-v1-controller.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.1.7.3, at inc/jwt/rest-api/version1/class-lp-rest-courses-v1-controller.php

1,407 lines 42.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 class LP_Jwt_Courses_V1_Controller extends LP_REST_Jwt_Posts_Controller {
3 protected $namespace = 'learnpress/v1';
4
5 protected $rest_base = 'courses';
6
7 protected $post_type = LP_COURSE_CPT;
8
9 protected $hierarchical = true;
10
11 public function register_routes() {
12 register_rest_route(
13 $this->namespace,
14 '/' . $this->rest_base,
15 array(
16 array(
17 'methods' => WP_REST_Server::READABLE,
18 'callback' => array( $this, 'get_items' ),
19 'permission_callback' => array( $this, 'get_items_permissions_check' ),
20 'args' => $this->get_collection_params(),
21 ),
22 array(
23 'methods' => WP_REST_Server::CREATABLE,
24 'callback' => array( $this, 'create_item' ),
25 'permission_callback' => array( $this, 'create_item_permissions_check' ),
26 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
27 ),
28 'schema' => array( $this, 'get_public_item_schema' ),
29 )
30 );
31
32 register_rest_route(
33 $this->namespace,
34 '/' . $this->rest_base . '/(?P<id>[\d]+)',
35 array(
36 'args' => array(
37 'id' => array(
38 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ),
39 'type' => 'integer',
40 ),
41 ),
42 array(
43 'methods' => WP_REST_Server::READABLE,
44 'callback' => array( $this, 'get_item' ),
45 'permission_callback' => array( $this, 'get_item_permissions_check' ),
46 'args' => array(
47 'context' => $this->get_context_param(
48 array(
49 'default' => 'view',
50 )
51 ),
52 ),
53 ),
54 'schema' => array( $this, 'get_public_item_schema' ),
55 )
56 );
57
58 register_rest_route(
59 $this->namespace,
60 '/' . $this->rest_base . '/enroll',
61 array(
62 'args' => array(
63 'id' => array(
64 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ),
65 'type' => 'integer',
66 ),
67 ),
68 array(
69 'methods' => WP_REST_Server::CREATABLE,
70 'callback' => array( $this, 'enroll_course' ),
71 'permission_callback' => array( $this, 'get_course_need_login_check' ),
72 'args' => array(
73 'context' => $this->get_context_param(
74 array(
75 'default' => 'edit',
76 )
77 ),
78 ),
79 ),
80 )
81 );
82
83 register_rest_route(
84 $this->namespace,
85 '/' . $this->rest_base . '/finish',
86 array(
87 'args' => array(
88 'id' => array(
89 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ),
90 'type' => 'integer',
91 ),
92 ),
93 array(
94 'methods' => WP_REST_Server::CREATABLE,
95 'callback' => array( $this, 'finish_course' ),
96 'permission_callback' => '__return_true',
97 'args' => array(
98 'context' => $this->get_context_param(
99 array(
100 'default' => 'edit',
101 )
102 ),
103 ),
104 ),
105 )
106 );
107
108 register_rest_route(
109 $this->namespace,
110 '/' . $this->rest_base . '/retake',
111 array(
112 'args' => array(
113 'id' => array(
114 'description' => esc_html__( 'A unique identifier for the resource.', 'learnpress' ),
115 'type' => 'integer',
116 ),
117 ),
118 array(
119 'methods' => WP_REST_Server::CREATABLE,
120 'callback' => array( $this, 'retake_course' ),
121 'permission_callback' => '__return_true',
122 'args' => array(
123 'context' => $this->get_context_param(
124 array(
125 'default' => 'edit',
126 )
127 ),
128 ),
129 ),
130 )
131 );
132
133 register_rest_route(
134 $this->namespace,
135 '/' . $this->rest_base . '/verify-receipt',
136 array(
137 'methods' => WP_REST_Server::CREATABLE,
138 'callback' => array( $this, 'verify_receipt' ),
139 'permission_callback' => '__return_true',
140 'args' => array(
141 'context' => $this->get_context_param(
142 array(
143 'default' => 'edit',
144 )
145 ),
146 'receipt-data' => array(
147 'description' => esc_html__( 'Receipt data.', 'learnpress' ),
148 'type' => 'string',
149 ),
150 ),
151 )
152 );
153 }
154
155 public function get_items_permissions_check( $request ) {
156 $post_type = get_post_type_object( $this->post_type );
157
158 if ( 'edit' === $request['context'] && ! current_user_can( $post_type->cap->edit_posts ) ) {
159 return new WP_Error(
160 'rest_forbidden_context',
161 __( 'Sorry, you are not allowed to edit posts in this post type.' ),
162 array( 'status' => rest_authorization_required_code() )
163 );
164 }
165
166 return true;
167 }
168
169 public function get_course_need_login_check( $request ) {
170 if ( ! is_user_logged_in() ) {
171 return new WP_Error(
172 'rest_forbidden_context',
173 __( 'Please login to continue' ),
174 array( 'status' => 401 )
175 );
176 }
177
178 return true;
179 }
180
181 /**
182 * Checks if a course can be read.
183 *
184 * Correctly handles courses with the inherit status.
185 *
186 * @author Nhamdv
187 *
188 * @return bool Whether the post can be read.
189 * */
190 public function check_read_permission( $post_id ) {
191 if ( empty( absint( $post_id ) ) ) {
192 return false;
193 }
194
195 $post = get_post( $post_id );
196
197 if ( ! $post ) {
198 return false;
199 }
200
201 // Is the post readable?
202 if ( 'publish' === $post->post_status || current_user_can( 'read_post', $post->ID ) ) {
203 return true;
204 }
205
206 $post_status_obj = get_post_status_object( $post->post_status );
207 if ( $post_status_obj && $post_status_obj->public ) {
208 return true;
209 }
210
211 // Can we read the parent if we're inheriting?
212 if ( 'inherit' === $post->post_status && $post->post_parent > 0 ) {
213 $parent = get_post( $post->post_parent );
214
215 if ( $parent ) {
216 return $this->check_read_permission( $parent );
217 }
218 }
219
220 /*
221 * If there isn't a parent, but the status is set to inherit, assume
222 * it's published (as per get_post_status()).
223 */
224 if ( 'inherit' === $post->post_status ) {
225 return true;
226 }
227
228 return false;
229 }
230
231 protected function get_object( $course = 0 ) {
232 global $post;
233
234 if ( false === $course && isset( $post, $post->ID ) && LP_COURSE_CPT === get_post_type( $post->ID ) ) {
235 $id = absint( $post->ID );
236 } elseif ( is_numeric( $course ) ) {
237 $id = $course;
238 } elseif ( $course instanceof LP_Course ) {
239 $id = $course->get_id();
240 } elseif ( ! empty( $course->ID ) ) {
241 $id = $course->ID;
242 }
243
244 return learn_press_get_course( $id );
245 }
246
247 public function verify_receipt( $request ) {
248 $response = new LP_REST_Response();
249 $receipt = ! empty( $request['receipt-data'] ) ? $request['receipt-data'] : '';
250 $is_ios = ! empty( $request['is-ios'] ) ? true : false;
251 $password = LP_Settings::instance()->get( 'in_app_purchase_apple_shared_secret', '' );
252
253 try {
254 if ( empty( $receipt ) ) {
255 throw new Exception( __( 'Receipt data is empty.', 'learnpress' ) );
256 }
257
258 if ( $is_ios ) {
259 $course_id = ! empty( $request['course-id'] ) ? absint( $request['course-id'] ) : 0;
260
261 if ( empty( $password ) ) {
262 throw new Exception( __( 'The secret key is empty.', 'learnpress' ) );
263 }
264
265 $url = LP_Settings::instance()->get( 'in_app_purchase_apple_sandbox' ) === 'yes' ? 'https://sandbox.itunes.apple.com/verifyReceipt' : 'https://buy.itunes.apple.com/verifyReceipt';
266
267 $verify = wp_remote_post(
268 $url,
269 array(
270 'method' => 'POST',
271 'timeout' => 60,
272 'body' => wp_json_encode(
273 array(
274 'receipt-data' => $receipt,
275 'password' => $password,
276 )
277 ),
278 )
279 );
280
281 if ( is_wp_error( $verify ) ) {
282 throw new Exception( $verify->get_error_message() );
283 }
284
285 $body = json_decode( wp_remote_retrieve_body( $verify ) );
286
287 if ( $body->status !== 0 ) {
288 throw new Exception( __( 'Cannot verify the receipt', 'learnpress' ) );
289 }
290
291 $latest_receipt_info = ! empty( $body->latest_receipt_info ) ? $body->latest_receipt_info : array();
292
293 if ( empty( $latest_receipt_info ) ) {
294 throw new Exception( __( 'The course ID is invalid.', 'learnpress' ) );
295 }
296
297 $course_ids = array_map(
298 function( $receipt_id ) {
299 return absint( $receipt_id->product_id );
300 },
301 $latest_receipt_info
302 );
303
304 if ( ! in_array( $course_id, $course_ids ) ) {
305 throw new Exception( __( 'The course ID is invalid.', 'learnpress' ) );
306 }
307 } else {
308 $receipt = json_decode( $receipt, true );
309 $package_name = $receipt['packageName'] ?? '';
310 $course_id = ! empty( $receipt['productId'] ) ? absint( $receipt['productId'] ) : 0;
311 $purchase_token = $receipt['purchaseToken'] ?? '';
312
313 if ( ! function_exists( 'learnpress_in_app_purchase_get_access_token' ) ) {
314 throw new Exception( __( 'Cannot verify the receipt', 'learnpress' ) );
315 }
316
317 $access_token = learnpress_in_app_purchase_get_access_token();
318
319 $verify = wp_remote_get( 'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/' . $package_name . '/purchases/products/' . $course_id . '/tokens/' . $purchase_token . '?access_token=' . $access_token );
320
321 $body = json_decode( wp_remote_retrieve_body( $verify ) );
322
323 if ( isset( $body->error->message ) ) {
324 throw new Exception( $body->error->message );
325 }
326 }
327
328 $course = learn_press_get_course( $course_id );
329
330 if ( ! $course ) {
331 throw new Exception( __( 'The course ID is invalid.', 'learnpress' ) );
332 }
333 $user = learn_press_get_current_user();
334
335 $cart = LearnPress::instance()->cart;
336 $checkout = LP_Checkout::instance();
337
338 if ( ! learn_press_enable_cart() ) {
339 $order_awaiting_payment = LearnPress::instance()->session->order_awaiting_payment;
340 $cart->empty_cart();
341 LearnPress::instance()->session->order_awaiting_payment = $order_awaiting_payment;
342 }
343
344 $cart_id = $cart->add_to_cart( $course_id, 1, array() );
345
346 if ( ! $cart_id ) {
347 throw new Exception( esc_html__( 'Error: The course cannot be added to the cart.', 'learnpress' ) );
348 }
349
350 if ( is_user_logged_in() ) {
351 $order_id = $checkout->create_order();
352
353 if ( is_wp_error( $order_id ) ) {
354 throw new Exception( $order_id->get_error_message() );
355 }
356
357 $order = new LP_Order( $order_id );
358
359 $order->payment_complete();
360
361 $cart->empty_cart();
362 }
363
364 $response->status = 'success';
365 $response->message = esc_html__( 'Verify Receipt Data successfully.', 'learnpress' );
366 } catch ( \Throwable $th ) {
367 $response->status = 'error';
368 $response->message = $th->getMessage();
369 }
370
371 return rest_ensure_response( $response );
372 }
373
374 public function enroll_course( $request ) {
375 if ( ! class_exists( 'LP_REST_Courses_Controller' ) ) {
376 include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-courses-controller.php';
377 }
378
379 $course_controller = new LP_REST_Courses_Controller();
380
381 return $course_controller->enroll_courses( $request );
382 }
383
384 public function finish_course( $request ) {
385 $response = new LP_REST_Response();
386
387 try {
388 $user = learn_press_get_current_user();
389 $course_id = isset( $request['id'] ) ? wp_unslash( $request['id'] ) : false;
390
391 if ( empty( $course_id ) ) {
392 throw new Exception( esc_html__( 'Error: No Course ID available.', 'learnpress' ) );
393 }
394
395 $course = learn_press_get_course( $course_id );
396 $check = $user->can_show_finish_course_btn( $course );
397
398 if ( $check['status'] !== 'success' ) {
399 throw new Exception( $check['message'] ?? esc_html__( 'Cannot finish this course.', 'learnpress' ) );
400 }
401
402 $finished = $user->finish_course( $course_id );
403
404 if ( empty( $finished ) ) {
405 throw new Exception( esc_html__( 'Error: Cannot finish this course.', 'learnpress' ) );
406 }
407
408 $response->status = 'success';
409 $response->message = esc_html__( 'Congrats! You have completed the Course.', 'learnpress' );
410 } catch ( \Throwable $th ) {
411 $response->status = 'error';
412 $response->message = $th->getMessage();
413 }
414
415 return rest_ensure_response( $response );
416 }
417
418 public function retake_course( $request ) {
419 $response = new LP_REST_Response();
420
421 if ( ! class_exists( 'LP_REST_Courses_Controller' ) ) {
422 include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-courses-controller.php';
423 }
424
425 $course_controller = new LP_REST_Courses_Controller();
426
427 return $course_controller->retake_course( $request );
428 }
429
430 /**
431 * Create a single product.
432 *
433 * @param WP_REST_Request $request Full details about the request.
434 * @return WP_Error|WP_REST_Response
435 */
436 public function create_item( $request ) {
437 if ( ! empty( $request['id'] ) ) {
438 return new WP_Error( "lp_rest_{$this->post_type}_exists", sprintf( __( 'Cannot create existing %s.', 'learnpress' ), $this->post_type ), array( 'status' => 400 ) );
439 }
440
441 $prepared_post = $this->prepare_item_for_database( $request );
442
443 if ( is_wp_error( $prepared_post ) ) {
444 return $prepared_post;
445 }
446
447 $prepared_post->post_type = $this->post_type;
448
449 $post_id = wp_insert_post( wp_slash( (array) $prepared_post ), true, false );
450
451 if ( is_wp_error( $post_id ) ) {
452 if ( 'db_insert_error' === $post_id->get_error_code() ) {
453 $post_id->add_data( array( 'status' => 500 ) );
454 } else {
455 $post_id->add_data( array( 'status' => 400 ) );
456 }
457
458 return $post_id;
459 }
460
461 $post = get_post( $post_id );
462
463 do_action( "lp_rest_insert_{$this->post_type}", $post, $request, true );
464
465 $fields_update = $this->update_additional_fields_for_object( $post, $request );
466
467 if ( is_wp_error( $fields_update ) ) {
468 return $fields_update;
469 }
470
471 $request->set_param( 'context', 'edit' );
472
473 do_action( "lp_rest_after_insert_{$this->post_type}", $post, $request, true );
474
475 wp_after_insert_post( $post, false, null );
476
477 $object = $this->get_object( $post_id );
478
479 $response = $this->prepare_object_for_response( $object, $request );
480 $response = rest_ensure_response( $response );
481
482 $response->set_status( 201 );
483 $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ) );
484
485 return $response;
486 }
487
488 public function prepare_object_for_response( $object, $request ) {
489 $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
490 $data = $this->get_course_data( $object, $context, $request );
491
492 $response = rest_ensure_response( $data );
493
494 return apply_filters( "lp_jwt_rest_prepare_{$this->post_type}_object", $response, $object, $request );
495 }
496
497 /**
498 * @param LP_Course $course
499 *
500 * @throws Exception
501 */
502 protected function get_course_data( $course, $context = 'view' ) {
503 $request = func_num_args() >= 2 ? func_get_arg( 2 ) : new WP_REST_Request( '', '', array( 'context' => $context ) );
504 $fields = $this->get_fields_for_response( $request );
505
506 $id = $course->get_id();
507 $post = get_post( $course->get_id() );
508
509 $data = array();
510
511 foreach ( $fields as $field ) {
512 if ( ! empty( $request['optimize'] ) ) {
513 $disables = is_bool( $request['optimize'] ) ? 'sections,course_data,instructor,meta_data,tags,can_finish,can_retake,count_students,rataken,ratake_count' : $request['optimize'];
514 $disable = explode( ',', $disables );
515
516 if ( ! empty( $disable ) && in_array( $field, $disable ) ) {
517 continue;
518 }
519 }
520
521 switch ( $field ) {
522 case 'id':
523 $data['id'] = $course->get_id();
524 break;
525 case 'name':
526 $data['name'] = $post->post_title;
527 break;
528 case 'slug':
529 $data['slug'] = $post->post_name;
530 break;
531 case 'permalink':
532 $data['permalink'] = $course->get_permalink();
533 break;
534 case 'image':
535 $data['image'] = $course->get_image_url( 'full' );
536 break;
537 case 'date_created':
538 $data['date_created'] = lp_jwt_prepare_date_response( $post->post_date_gmt, $post->post_date );
539 break;
540 case 'date_created_gmt':
541 $data['date_created_gmt'] = lp_jwt_prepare_date_response( $post->post_date_gmt );
542 break;
543 case 'date_modified':
544 $data['date_modified'] = lp_jwt_prepare_date_response( $post->post_modified_gmt, $post->post_modified );
545 break;
546 case 'date_modified_gmt':
547 $data['date_modified_gmt'] = lp_jwt_prepare_date_response( $post->post_modified_gmt );
548 break;
549 case 'on_sale':
550 $data['on_sale'] = $course->has_sale_price();
551 break;
552 case 'status':
553 $data['status'] = $post->post_status;
554 break;
555 case 'content':
556 $data['content'] = 'view' === $context ? apply_filters( 'the_content', $post->post_content ) : $post->post_content;
557 break;
558 case 'excerpt':
559 $data['excerpt'] = $post->post_excerpt;
560 break;
561 case 'count_students':
562 $data['count_students'] = $course->count_students();
563 break;
564 case 'can_finish':
565 $data['can_finish'] = $this->check_can_finish( $course );
566 break;
567 case 'can_retake':
568 $data['can_retake'] = $this->check_can_retake( $id );
569 break;
570 case 'ratake_count':
571 $data['ratake_count'] = (int) $course->get_data( 'retake_count' );
572 break;
573 case 'rataken':
574 $data['rataken'] = $this->get_retaken_count( $id );
575 break;
576 case 'duration':
577 $data['duration'] = learn_press_get_post_translated_duration( $id, esc_html__( 'Lifetime', 'learnpress' ) );
578 break;
579 case 'categories':
580 $data['categories'] = $this->get_course_taxonomy( $id, 'course_category' );
581 break;
582 case 'tags':
583 $data['tags'] = $this->get_course_taxonomy( $id, 'course_tag' );
584 break;
585 case 'instructor':
586 $data['instructor'] = $this->get_instructor_info( $id, $request, $course );
587 break;
588 case 'sections':
589 $data['sections'] = $this->get_all_items( $course );
590 break;
591 case 'course_data':
592 $data['course_data'] = $this->get_course_data_for_current_user( $id, $request );
593 break;
594 case 'rating':
595 $data['rating'] = $this->get_course_rating( $id );
596 break;
597 case 'price':
598 $data['price'] = floatval( $course->get_price() );
599 break;
600 case 'price_rendered':
601 $data['price_rendered'] = html_entity_decode( $course->get_price_html() );
602 break;
603 case 'origin_price':
604 $data['origin_price'] = floatval( $course->get_origin_price() );
605 break;
606 case 'origin_price_rendered':
607 $data['origin_price_rendered'] = html_entity_decode( $course->get_origin_price_html() );
608 break;
609 case 'sale_price':
610 $data['sale_price'] = floatval( $course->get_sale_price() );
611 break;
612 case 'sale_price_rendered':
613 $data['sale_price_rendered'] = html_entity_decode( learn_press_format_price( $course->get_sale_price(), true ) );
614 break;
615 }
616 }
617
618 $data['meta_data'] = $this->get_course_meta( $id );
619
620 return $data;
621 }
622
623 public function get_retaken_count( $id ) {
624 $user_id = get_current_user_id();
625
626 if ( ! $user_id ) {
627 return 0;
628 }
629
630 $user = learn_press_get_user( $user_id );
631
632 if ( ! $user ) {
633 return 0;
634 }
635
636 $user_course_data = $user->get_course_data( $id );
637 if ( ! $user_course_data ) {
638 return 0;
639 }
640
641 return absint( $user_course_data->get_retaken_count() );
642 }
643
644 public function check_can_retake( $id ) {
645 $user_id = get_current_user_id();
646
647 if ( ! $user_id ) {
648 return 0;
649 }
650
651 $user = learn_press_get_user( $user_id );
652
653 if ( $user ) {
654 $can_retake_times = $user->can_retry_course( $id );
655
656 if ( $can_retake_times ) {
657 return true;
658 }
659
660 return false;
661 }
662
663 return false;
664 }
665
666 public function get_course_rating( $id ) {
667 if ( ! function_exists( 'learn_press_get_course_rate' ) ) {
668 return false;
669 }
670
671 $course_rate = learn_press_get_course_rate( $id );
672
673 return ! empty( $course_rate ) ? floatval( number_format( $course_rate, 1 ) ) : 0;
674 }
675
676 public function check_can_finish( $course ) {
677 $user_id = get_current_user_id();
678
679 if ( ! $user_id ) {
680 return false;
681 }
682
683 $user = learn_press_get_user( $user_id );
684
685 if ( $user && $course ) {
686 $check = $user->can_show_finish_course_btn( $course );
687
688 if ( $check['status'] === 'success' ) {
689 return true;
690 }
691
692 return false;
693 }
694
695 return false;
696 }
697
698 public function get_instructor_info( $id, $request, $course ) {
699 $user_id = get_post_meta( $id, '_lp_course_author', true );
700
701 $output = array();
702
703 $extra_info = learn_press_get_user_extra_profile_info( $user_id );
704
705 $instructor = $course->get_instructor();
706
707 $output['avatar'] = $instructor->get_upload_profile_src();
708
709 if ( $user_id ) {
710 $user = get_user_by( 'ID', absint( $user_id ) );
711
712 if ( $user ) {
713 $output['id'] = absint( $user_id );
714 $output['name'] = $user->display_name;
715 $output['description'] = $user->description;
716 $output['social'] = $extra_info;
717 }
718 }
719
720 return $output;
721 }
722
723 public function get_item_learned_ids( $request ) {
724 global $wpdb;
725
726 $user_id = get_current_user_id();
727
728 if ( ! $user_id ) {
729 return false;
730 }
731
732 $filter = ! empty( $request['course_filter'] ) ? $request['course_filter'] : false;
733 $where = $wpdb->prepare( 'user_id=%d AND item_type=%s', $user_id, $this->post_type ); // phpcs:ignore
734
735 if ( $filter ) {
736 if ( $filter === 'in-progress' ) {
737 $where .= $wpdb->prepare( ' AND status=%s AND graduation=%s', 'enrolled', 'in-progress' );
738 } elseif ( in_array( $filter, array( 'passed', 'failed' ) ) ) { // is "passed" or "failed"
739 $where .= $wpdb->prepare( ' AND status=%s AND graduation=%s', 'finished', $filter );
740 }
741 }
742
743 $query = "SELECT item_id FROM {$wpdb->prefix}learnpress_user_items WHERE {$where}";
744
745 $item_ids = $wpdb->get_col( $query );
746
747 return $item_ids;
748 }
749
750 public function get_course_data_for_current_user( $id, $request ) {
751 $user = learn_press_get_user( get_current_user_id() );
752
753 if ( empty( $user ) || empty( $id ) ) {
754 return array();
755 }
756
757 $course_data = $user->get_course_data( $id );
758
759 if ( ! $course_data ) {
760 return;
761 }
762
763 return array(
764 'graduation' => $course_data->get_graduation() ?? '',
765 'status' => $course_data->get_status() ?? '',
766 'start_time' => $course_data->get_start_time() ? lp_jwt_prepare_date_response( $course_data->get_start_time()->toSql( false ) ) : null,
767 'end_time' => $course_data->get_end_time() ? lp_jwt_prepare_date_response( $course_data->get_end_time()->toSql( false ) ) : null,
768 'expiration_time' => $course_data->get_expiration_time() ? lp_jwt_prepare_date_response( $course_data->get_expiration_time()->toSql( false ) ) : '',
769 'result' => $course_data->calculate_course_results(),
770 );
771 }
772
773 public function get_course_taxonomy( $id, $taxonomy ) {
774 $terms = get_the_terms( $id, $taxonomy );
775 $output = array();
776
777 if ( $terms ) {
778 foreach ( $terms as $term ) {
779 $output[] = array(
780 'id' => $term->term_id,
781 'name' => $term->name,
782 'slug' => $term->slug,
783 );
784 }
785 }
786
787 return $output;
788 }
789
790 /**
791 * Get Items of sections
792 *
793 * @throws Exception
794 * @editor tungnx
795 * @modify 4.1.3
796 * @version 4.0.1
797 */
798 public function get_all_items( $course ): array {
799 $curriculum = $course->get_curriculum();
800 $user = learn_press_get_current_user();
801 $user_course = $user ? $user->get_course_data( $course->get_id() ) : false;
802 $output = array();
803
804 if ( ! empty( $curriculum ) ) {
805 foreach ( $curriculum as $section ) {
806 if ( $section ) {
807 $data = array(
808 'id' => $section->get_id(),
809 'title' => $section->get_title(),
810 'course_id' => $section->get_course_id(),
811 'description' => $section->get_description(),
812 'order' => $section->get_order(),
813 );
814
815 if ( $user_course && $user->has_enrolled_or_finished( $section->get_course_id() ) ) {
816 $data['percent'] = $user_course->get_percent_completed_items( '', $section->get_id() );
817 }
818
819 $data_item = array();
820
821 $items = $section->get_items();
822
823 if ( ! empty( $items ) ) {
824 foreach ( $items as $item ) {
825 $post = get_post( $item->get_id() );
826
827 $format = array(
828 'day' => __( '%s days', 'learnpress' ),
829 'hour' => __( '%s hours', 'learnpress' ),
830 'minute' => __( '%s mins', 'learnpress' ),
831 'second' => __( '%s secs', 'learnpress' ),
832 );
833
834 $user_item = $user_course ? $user_course->get_item( $item->get_id() ) : false;
835
836 if ( $user ) {
837 $can_view_content_course = $user->can_view_content_course( absint( $section->get_course_id() ) );
838 $can_view_item = $user->can_view_item( $item->get_id(), $can_view_content_course );
839 }
840
841 $data_item[] = array(
842 'id' => $item->get_id(),
843 'type' => $item->get_item_type(),
844 'title' => $post->post_title,
845 'preview' => $item->is_preview(),
846 'duration' => $item->get_duration()->to_timer( $format, true ),
847 'graduation' => $user_item ? $user_item->get_graduation() : '',
848 'status' => $user_item ? $user_item->get_status() : '',
849 'locked' => isset( $can_view_item->flag ) ? ! $can_view_item->flag : true,
850 );
851 }
852 }
853
854 $data['items'] = $data_item;
855 $output[] = $data;
856 }
857 }
858 }
859
860 return $output;
861 }
862
863 public function get_course_meta( $id ) {
864 $user_id = get_current_user_id();
865
866 if ( ! $user_id ) {
867 return array();
868 }
869
870 if ( ! class_exists( 'LP_Meta_Box' ) ) {
871 include_once LP_PLUGIN_PATH . 'inc/admin/views/meta-boxes/class-lp-meta-box.php';
872 }
873
874 if ( ! class_exists( 'LP_Meta_Box_Course' ) ) {
875 include_once LP_PLUGIN_PATH . 'inc/admin/views/meta-boxes/course/settings.php';
876 }
877
878 $metabox = new LP_Meta_Box_Course();
879
880 $output = array();
881
882 foreach ( $metabox->metabox( $id ) as $key => $tab ) {
883 if ( isset( $tab['content'] ) ) {
884 foreach ( $tab['content'] as $meta_key => $object ) {
885 if ( is_a( $object, 'LP_Meta_Box_Field' ) ) {
886 $object->id = $meta_key;
887 $output[ $meta_key ] = $object->meta_value( $id );
888 }
889 }
890 }
891 }
892
893 return $output;
894 }
895
896 protected function prepare_objects_query( $request ) {
897 $args = parent::prepare_objects_query( $request );
898
899 $taxonomies = array(
900 'course_category' => 'category',
901 'course_tag' => 'tag',
902 );
903
904 foreach ( $taxonomies as $taxonomy => $key ) {
905 if ( ! empty( $request[ $key ] ) ) {
906 $tax_query[] = array(
907 'taxonomy' => $taxonomy,
908 'field' => 'term_id',
909 'terms' => $request[ $key ],
910 );
911 }
912 }
913
914 if ( ! empty( $tax_query ) ) {
915 $args['tax_query'] = $tax_query;
916 }
917
918 $orderby = $request->get_param( 'orderby' );
919 $order = $request->get_param( 'order' );
920
921 $orderby = strtolower( is_array( $orderby ) ? (string) current( $orderby ) : (string) $orderby );
922 $order = strtoupper( is_array( $order ) ? (string) current( $order ) : (string) $order );
923
924 switch ( $orderby ) {
925 case 'id':
926 $args['orderby'] = 'ID';
927 break;
928 case 'menu_order':
929 $args['orderby'] = 'menu_order title';
930 break;
931 case 'title':
932 $args['orderby'] = 'post_title';
933 $args['order'] = ( 'DESC' === $order ) ? 'DESC' : 'ASC';
934 break;
935 case 'relevance':
936 $args['orderby'] = 'relevance';
937 $args['order'] = 'DESC';
938 break;
939 case 'rand':
940 $args['orderby'] = 'rand'; // @codingStandardsIgnoreLine
941 break;
942 case 'date':
943 $args['orderby'] = 'date ID';
944 $args['order'] = ( 'ASC' === $order ) ? 'ASC' : 'DESC';
945 break;
946 case 'price':
947 $args['orderby'] = 'meta_value_num';
948 $args['meta_key'] = '_lp_price';
949 break;
950 }
951
952 if ( is_bool( $request['on_sale'] ) ) {
953 $on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in';
954
955 $filter = new LP_Course_Filter();
956 $filter->only_fields = array( 'ID' );
957
958 $filter = LP_Course_DB::getInstance()->get_courses_sort_by_sale( $filter );
959 $on_sale_ids = LP_Course_DB::getInstance()->get_courses( $filter );
960 $on_sale_ids = LP_Database::get_values_by_key( $on_sale_ids );
961 $on_sale_ids = empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids;
962
963 $args[ $on_sale_key ] += $on_sale_ids;
964 } elseif ( is_bool( $request['popular'] ) ) {
965 $on_popular_key = $request['popular'] ? 'post__in' : 'post__not_in';
966
967 $filter = new LP_Course_Filter();
968 $filter->only_fields = array( 'ID' );
969 $filter->limit = $request['per_page'] ?? 10;
970 $filter->page = $request['page'] ?? 1;
971
972 $filter = LP_Course_DB::getInstance()->get_courses_order_by_popular( $filter );
973 $on_popular_ids = LP_Course_DB::getInstance()->get_courses( $filter );
974 $on_popular_ids = LP_Database::get_values_by_key( $on_popular_ids );
975
976 $on_popular_ids = empty( $on_popular_ids ) ? array( 0 ) : $on_popular_ids;
977
978 $args[ $on_popular_key ] += $on_popular_ids;
979
980 $args['orderby'] = 'post__in';
981 }
982
983 return $args;
984 }
985
986 public function get_item_schema() {
987 $schema = array(
988 '$schema' => 'http://json-schema.org/draft-04/schema#',
989 'title' => $this->post_type,
990 'type' => 'object',
991 'properties' => array(
992 'id' => array(
993 'description' => __( 'A unique identifier for the resource.', 'learnpress' ),
994 'type' => 'integer',
995 'context' => array( 'view', 'edit' ),
996 'readonly' => true,
997 ),
998 'name' => array(
999 'description' => __( 'Course name.', 'learnpress' ),
1000 'type' => 'string',
1001 'context' => array( 'view', 'edit' ),
1002 ),
1003 'slug' => array(
1004 'description' => __( 'Course slug.', 'learnpress' ),
1005 'type' => 'string',
1006 'context' => array( 'view', 'edit' ),
1007 ),
1008 'permalink' => array(
1009 'description' => __( 'Course URL.', 'learnpress' ),
1010 'type' => 'string',
1011 'format' => 'uri',
1012 'context' => array( 'view', 'edit' ),
1013 'readonly' => true,
1014 ),
1015 'image' => array(
1016 'description' => __( 'Course Image URL.', 'learnpress' ),
1017 'type' => 'string',
1018 'format' => 'uri',
1019 'context' => array( 'view', 'edit' ),
1020 ),
1021 'date_created' => array(
1022 'description' => __( "The date the Course was created, in the site's timezone.", 'learnpress' ),
1023 'type' => array( 'string', 'null' ),
1024 'format' => 'date-time',
1025 'context' => array( 'view', 'edit' ),
1026 'readonly' => true,
1027 ),
1028 'date_created_gmt' => array(
1029 'description' => __( 'The date the Course was created, as GMT.', 'learnpress' ),
1030 'type' => array( 'string', 'null' ),
1031 'format' => 'date-time',
1032 'context' => array( 'view', 'edit' ),
1033 'readonly' => true,
1034 ),
1035 'date_modified' => array(
1036 'description' => __( "The date the Course was last modified, in the site's timezone.", 'learnpress' ),
1037 'type' => array( 'string', 'null' ),
1038 'format' => 'date-time',
1039 'context' => array( 'view', 'edit' ),
1040 'readonly' => true,
1041 ),
1042 'date_modified_gmt' => array(
1043 'description' => __( 'The date the Course was last modified, as GMT.', 'learnpress' ),
1044 'type' => array( 'string', 'null' ),
1045 'format' => 'date-time',
1046 'context' => array( 'view', 'edit' ),
1047 'readonly' => true,
1048 ),
1049 'on_sale' => array(
1050 'description' => __( 'Display courses if they are on sale.', 'learnpress' ),
1051 'type' => 'boolean',
1052 'context' => array( 'view', 'edit' ),
1053 'readonly' => true,
1054 ),
1055 'status' => array(
1056 'description' => __( 'Course status (post status).', 'learnpress' ),
1057 'type' => 'string',
1058 'default' => 'publish',
1059 'enum' => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ),
1060 'context' => array( 'view', 'edit' ),
1061 ),
1062 'content' => array(
1063 'description' => __( 'Course content.', 'learnpress' ),
1064 'type' => 'string',
1065 'context' => array( 'view', 'edit' ),
1066 ),
1067 'excerpt' => array(
1068 'description' => __( 'Retrieves the course excerpt..', 'learnpress' ),
1069 'type' => 'string',
1070 'context' => array( 'view', 'edit' ),
1071 ),
1072 'duration' => array(
1073 'description' => __( 'Duration', 'learnpress' ),
1074 'type' => 'string',
1075 'context' => array( 'view' ),
1076 ),
1077 'count_students' => array(
1078 'description' => __( 'Count the number of enrolled students.', 'learnpress' ),
1079 'type' => 'integer',
1080 'context' => array( 'view' ),
1081 'readonly' => true,
1082 ),
1083 'can_finish' => array(
1084 'description' => __( 'Can finish the course', 'learnpress' ),
1085 'type' => 'boolean',
1086 'context' => array( 'view' ),
1087 'readonly' => true,
1088 ),
1089 'can_retake' => array(
1090 'description' => __( 'Can retake the course', 'learnpress' ),
1091 'type' => 'boolean',
1092 'context' => array( 'view' ),
1093 'readonly' => true,
1094 ),
1095 'ratake_count' => array(
1096 'description' => __( 'Total retakes', 'learnpress' ),
1097 'type' => 'integer',
1098 'context' => array( 'view' ),
1099 'readonly' => true,
1100 ),
1101 'rataken' => array(
1102 'description' => __( 'Retaken', 'learnpress' ),
1103 'type' => 'integer',
1104 'context' => array( 'view' ),
1105 'readonly' => true,
1106 ),
1107 'rating' => array(
1108 'description' => __( 'Course Review add-on', 'learnpress' ),
1109 'type' => array( 'boolean', 'integer' ),
1110 'context' => array( 'view' ),
1111 'readonly' => true,
1112 ),
1113 'price' => array(
1114 'description' => __( 'Course Price', 'learnpress' ),
1115 'type' => 'integer',
1116 'context' => array( 'view' ),
1117 'readonly' => true,
1118 ),
1119 'price_rendered' => array(
1120 'description' => __( 'Course Price Rendered', 'learnpress' ),
1121 'type' => 'string',
1122 'context' => array( 'view' ),
1123 'readonly' => true,
1124 ),
1125 'origin_price' => array(
1126 'description' => __( 'Course Origin Price', 'learnpress' ),
1127 'type' => 'integer',
1128 'context' => array( 'view' ),
1129 'readonly' => true,
1130 ),
1131 'origin_price_rendered' => array(
1132 'description' => __( 'Course Origin Price Rendered', 'learnpress' ),
1133 'type' => 'string',
1134 'context' => array( 'view' ),
1135 'readonly' => true,
1136 ),
1137 'sale_price' => array(
1138 'description' => __( 'Course Sale Price', 'learnpress' ),
1139 'type' => 'integer',
1140 'context' => array( 'view' ),
1141 'readonly' => true,
1142 ),
1143 'sale_price_rendered' => array(
1144 'description' => __( 'Course Sale Price Rendered', 'learnpress' ),
1145 'type' => 'string',
1146 'context' => array( 'view' ),
1147 'readonly' => true,
1148 ),
1149 'categories' => array(
1150 'description' => __( 'List of categories.', 'learnpress' ),
1151 'type' => 'array',
1152 'context' => array( 'view', 'edit' ),
1153 'items' => array(
1154 'type' => 'object',
1155 'properties' => array(
1156 'id' => array(
1157 'description' => __( 'Category ID.', 'learnpress' ),
1158 'type' => 'integer',
1159 'context' => array( 'view', 'edit' ),
1160 ),
1161 'name' => array(
1162 'description' => __( 'Category name.', 'learnpress' ),
1163 'type' => 'string',
1164 'context' => array( 'view', 'edit' ),
1165 'readonly' => true,
1166 ),
1167 'slug' => array(
1168 'description' => __( 'Category slug.', 'learnpress' ),
1169 'type' => 'string',
1170 'context' => array( 'view', 'edit' ),
1171 'readonly' => true,
1172 ),
1173 ),
1174 ),
1175 ),
1176 'tags' => array(
1177 'description' => __( 'List of tags.', 'learnpress' ),
1178 'type' => 'array',
1179 'context' => array( 'view', 'edit' ),
1180 'items' => array(
1181 'type' => 'object',
1182 'properties' => array(
1183 'id' => array(
1184 'description' => __( 'Tag ID.', 'learnpress' ),
1185 'type' => 'integer',
1186 'context' => array( 'view', 'edit' ),
1187 ),
1188 'name' => array(
1189 'description' => __( 'Tag name.', 'learnpress' ),
1190 'type' => 'string',
1191 'context' => array( 'view', 'edit' ),
1192 'readonly' => true,
1193 ),
1194 'slug' => array(
1195 'description' => __( 'Tag slug.', 'learnpress' ),
1196 'type' => 'string',
1197 'context' => array( 'view', 'edit' ),
1198 'readonly' => true,
1199 ),
1200 ),
1201 ),
1202 ),
1203 'instructor' => array(
1204 'description' => __( 'Retrieves the course sections and items..', 'learnpress' ),
1205 'type' => 'array',
1206 'context' => array( 'view', 'edit' ),
1207 'items' => array(
1208 'type' => 'object',
1209 'properties' => array(
1210 'id' => array(
1211 'description' => __( 'User ID.', 'learnpress' ),
1212 'type' => 'integer',
1213 'context' => array( 'view' ),
1214 ),
1215 'name' => array(
1216 'description' => __( 'Display name.', 'learnpress' ),
1217 'type' => 'string',
1218 'context' => array( 'view' ),
1219 'readonly' => true,
1220 ),
1221 'description' => array(
1222 'description' => __( 'Tag slug.', 'learnpress' ),
1223 'type' => 'string',
1224 'context' => array( 'view' ),
1225 'readonly' => true,
1226 ),
1227 'social' => array(
1228 'description' => __( 'Social Infor.', 'learnpress' ),
1229 'type' => 'array',
1230 'context' => array( 'view' ),
1231 'readonly' => true,
1232 ),
1233 ),
1234 ),
1235 ),
1236 'sections' => array(
1237 'description' => __( 'Retrieves the course sections and items..', 'learnpress' ),
1238 'type' => 'array',
1239 'context' => array( 'view', 'edit' ),
1240 'items' => array(
1241 'type' => 'object',
1242 'properties' => array(
1243 'id' => array(
1244 'description' => __( 'Section ID.', 'learnpress' ),
1245 'type' => 'integer',
1246 'context' => array( 'view', 'edit' ),
1247 ),
1248 'title' => array(
1249 'description' => __( 'Section name.', 'learnpress' ),
1250 'type' => 'string',
1251 'context' => array( 'view', 'edit' ),
1252 ),
1253 'course_id' => array(
1254 'description' => __( 'Course ID.', 'learnpress' ),
1255 'type' => 'integer',
1256 'context' => array( 'view', 'edit' ),
1257 ),
1258 'description' => array(
1259 'description' => __( 'Section description.', 'learnpress' ),
1260 'type' => 'string',
1261 'context' => array( 'view', 'edit' ),
1262 ),
1263 'items' => array(
1264 'description' => __( 'Section items.', 'learnpress' ),
1265 'type' => 'array',
1266 'context' => array( 'view', 'edit' ),
1267 'items' => array(
1268 'id' => array(
1269 'description' => __( 'Item ID.', 'learnpress' ),
1270 'type' => 'integer',
1271 'context' => array( 'view', 'edit' ),
1272 ),
1273 'type' => array(
1274 'description' => __( 'Item Type.', 'learnpress' ),
1275 'type' => 'string',
1276 'context' => array( 'view', 'edit' ),
1277 ),
1278 'title' => array(
1279 'description' => __( 'Item title.', 'learnpress' ),
1280 'type' => 'string',
1281 'context' => array( 'view', 'edit' ),
1282 ),
1283 'preview' => array(
1284 'description' => __( 'Item ID.', 'learnpress' ),
1285 'type' => 'boolean',
1286 'context' => array( 'view', 'edit' ),
1287 ),
1288 'percent' => array(
1289 'description' => __( 'Percent.', 'learnpress' ),
1290 'type' => 'integer',
1291 'context' => array( 'view', 'edit' ),
1292 ),
1293 'duration' => array(
1294 'description' => __( 'Duration.', 'learnpress' ),
1295 'type' => 'string',
1296 'context' => array( 'view', 'edit' ),
1297 ),
1298 'graduation' => array(
1299 'description' => __( 'Graduation.', 'learnpress' ),
1300 'type' => 'string',
1301 'context' => array( 'view', 'edit' ),
1302 ),
1303 'status' => array(
1304 'description' => __( 'Status.', 'learnpress' ),
1305 'type' => 'string',
1306 'context' => array( 'view', 'edit' ),
1307 ),
1308 'locked' => array(
1309 'description' => __( 'Locked.', 'learnpress' ),
1310 'type' => 'boolean',
1311 'context' => array( 'view', 'edit' ),
1312 ),
1313 ),
1314 ),
1315 ),
1316 ),
1317 ),
1318 'course_data' => array(
1319 'description' => __( 'List of course user data.', 'learnpress' ),
1320 'type' => 'array',
1321 'context' => array( 'view' ),
1322 'items' => array(
1323 'type' => 'object',
1324 'properties' => array(
1325 'graduation' => array(
1326 'description' => __( 'Graduation', 'learnpress' ),
1327 'type' => 'string',
1328 'context' => array( 'view' ),
1329 ),
1330 'status' => array(
1331 'description' => __( 'Status', 'learnpress' ),
1332 'type' => 'string',
1333 'context' => array( 'view' ),
1334 'readonly' => true,
1335 ),
1336 'start_time' => array(
1337 'description' => __( 'Start time', 'learnpress' ),
1338 'type' => 'string',
1339 'context' => array( 'view' ),
1340 'readonly' => true,
1341 ),
1342 'end_time' => array(
1343 'description' => __( 'End time', 'learnpress' ),
1344 'type' => 'string',
1345 'context' => array( 'view' ),
1346 'readonly' => true,
1347 ),
1348 'expiration_time' => array(
1349 'description' => __( 'Expiration time', 'learnpress' ),
1350 'type' => 'string',
1351 'context' => array( 'view' ),
1352 'readonly' => true,
1353 ),
1354 ),
1355 ),
1356 ),
1357 ),
1358 );
1359
1360 return $this->add_additional_fields_schema( $schema );
1361 }
1362
1363 public function get_collection_params() {
1364 $params = parent::get_collection_params();
1365
1366 $params['orderby']['enum'] = array_merge( $params['orderby']['enum'], array( 'menu_order', 'price' ) );
1367
1368 $params['category'] = array(
1369 'description' => 'Limit the result set to courses assigned to a specific category ID.',
1370 'type' => 'string',
1371 'sanitize_callback' => 'wp_parse_id_list',
1372 'validate_callback' => 'rest_validate_request_arg',
1373 );
1374 $params['tag'] = array(
1375 'description' => 'Limit the result set to courses assigned to a specific tag ID.',
1376 'type' => 'string',
1377 'sanitize_callback' => 'wp_parse_id_list',
1378 'validate_callback' => 'rest_validate_request_arg',
1379 );
1380 $params['course_filter'] = array(
1381 'description' => 'Filter by course to in-progress, passed, failed.',
1382 'type' => 'string',
1383 'sanitize_callback' => 'sanitize_text_field',
1384 'validate_callback' => 'rest_validate_request_arg',
1385 );
1386
1387 $params['on_sale'] = array(
1388 'description' => 'Get item learned by user.',
1389 'type' => 'boolean',
1390 'validate_callback' => 'rest_validate_request_arg',
1391 );
1392
1393 $params['popular'] = array(
1394 'description' => 'Get item popularity.',
1395 'type' => 'boolean',
1396 'validate_callback' => 'rest_validate_request_arg',
1397 );
1398 $params['optimize'] = array(
1399 'description' => 'Disable some fields in the schema.',
1400 'type' => array( 'boolean', 'string' ),
1401 'validate_callback' => 'wp_parse_id_list',
1402 );
1403
1404 return $params;
1405 }
1406 }
1407