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

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