PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.5
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.5
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.4.5, at inc/jwt/rest-api/version1/class-lp-rest-courses-v1-controller.php

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