PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.10
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.10
4.4.10 4.4.9.1 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 All 142 releases
← All changes | inc/jwt/rest-api/version1/class-lp-rest-courses-v1-controller.php +408 -46 4.1.7.3 → 4.4.10 View file →
@@ -1,5 +1,13 @@
1 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 +
2 10 class LP_Jwt_Courses_V1_Controller extends LP_REST_Jwt_Posts_Controller {
3 11 protected $namespace = 'learnpress/v1';
4 12
5 13 protected $rest_base = 'courses';
@@ -14,9 +22,9 @@
14 22 '/' . $this->rest_base,
15 23 array(
16 24 array(
17 25 'methods' => WP_REST_Server::READABLE,
18 - 'callback' => array( $this, 'get_items' ),
26 + 'callback' => array( $this, 'get_courses' ),
19 27 'permission_callback' => array( $this, 'get_items_permissions_check' ),
20 28 'args' => $this->get_collection_params(),
21 29 ),
22 30 array(
@@ -182,12 +190,12 @@
182 190 * Checks if a course can be read.
183 191 *
184 192 * Correctly handles courses with the inherit status.
185 193 *
186 - * @author Nhamdv
194 + * @return bool Whether the post can be read.
195 + * *@author Nhamdv
187 196 *
188 - * @return bool Whether the post can be read.
189 - * */
197 + */
190 198 public function check_read_permission( $post_id ) {
191 199 if ( empty( absint( $post_id ) ) ) {
192 200 return false;
193 201 }
@@ -247,10 +255,13 @@
247 255 public function verify_receipt( $request ) {
248 256 $response = new LP_REST_Response();
249 257 $receipt = ! empty( $request['receipt-data'] ) ? $request['receipt-data'] : '';
250 258 $is_ios = ! empty( $request['is-ios'] ) ? true : false;
251 - $password = LP_Settings::instance()->get( 'in_app_purchase_apple_shared_secret', '' );
252 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 +
253 264 try {
254 265 if ( empty( $receipt ) ) {
255 266 throw new Exception( __( 'Receipt data is empty.', 'learnpress' ) );
256 267 }
@@ -294,9 +305,9 @@
294 305 throw new Exception( __( 'The course ID is invalid.', 'learnpress' ) );
295 306 }
296 307
297 308 $course_ids = array_map(
298 - function( $receipt_id ) {
309 + function ( $receipt_id ) {
299 310 return absint( $receipt_id->product_id );
300 311 },
301 312 $latest_receipt_info
302 313 );
@@ -313,11 +324,20 @@
313 324 if ( ! function_exists( 'learnpress_in_app_purchase_get_access_token' ) ) {
314 325 throw new Exception( __( 'Cannot verify the receipt', 'learnpress' ) );
315 326 }
316 327
317 - $access_token = learnpress_in_app_purchase_get_access_token();
328 + $access_token = function_exists( 'learnpress_mobile_iap_settings' ) ? learnpress_in_app_purchase_get_access_token( $platform ) : learnpress_in_app_purchase_get_access_token();
318 329
319 - $verify = wp_remote_get( 'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/' . $package_name . '/purchases/products/' . $course_id . '/tokens/' . $purchase_token . '?access_token=' . $access_token );
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 + );
320 340
321 341 $body = json_decode( wp_remote_retrieve_body( $verify ) );
322 342
323 343 if ( isset( $body->error->message ) ) {
@@ -335,11 +355,11 @@
335 355 $cart = LearnPress::instance()->cart;
336 356 $checkout = LP_Checkout::instance();
337 357
338 358 if ( ! learn_press_enable_cart() ) {
339 - $order_awaiting_payment = LearnPress::instance()->session->order_awaiting_payment;
359 + // $order_awaiting_payment = LearnPress::instance()->session->get( 'order_awaiting_payment' );
340 360 $cart->empty_cart();
341 - LearnPress::instance()->session->order_awaiting_payment = $order_awaiting_payment;
361 + // LearnPress::instance()->session->set( 'order_awaiting_payment', $order_awaiting_payment );
342 362 }
343 363
344 364 $cart_id = $cart->add_to_cart( $course_id, 1, array() );
345 365
@@ -348,17 +368,10 @@
348 368 }
349 369
350 370 if ( is_user_logged_in() ) {
351 371 $order_id = $checkout->create_order();
352 -
353 - if ( is_wp_error( $order_id ) ) {
354 - throw new Exception( $order_id->get_error_message() );
355 - }
356 -
357 - $order = new LP_Order( $order_id );
358 -
372 + $order = new LP_Order( $order_id );
359 373 $order->payment_complete();
360 -
361 374 $cart->empty_cart();
362 375 }
363 376
364 377 $response->status = 'success';
@@ -363,8 +376,9 @@
363 376
364 377 $response->status = 'success';
365 378 $response->message = esc_html__( 'Verify Receipt Data successfully.', 'learnpress' );
366 379 } catch ( \Throwable $th ) {
380 + error_log( $th->getMessage() );
367 381 $response->status = 'error';
368 382 $response->message = $th->getMessage();
369 383 }
370 384
@@ -370,8 +384,241 @@
370 384
371 385 return rest_ensure_response( $response );
372 386 }
373 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 +
464 + // Filter by slug (post_name).
465 + if ( ! empty( $params['slug'] ) ) {
466 + $filter->post_name = is_array( $params['slug'] ) ? reset( $params['slug'] ) : $params['slug'];
467 + }
468 +
469 + $key_cache = 'api/' . md5( json_encode( $params ) );
470 + $key_cache_total = $key_cache . '_total';
471 + $key_cache_total_pages = $key_cache . '_total_pages';
472 + $lp_courses_cache = new LP_Courses_Cache( true );
473 +
474 + if ( ! empty( $params['learned'] ) ) {
475 + $user_id = get_current_user_id();
476 + // For tst user hard code
477 + // $user_id = ! empty( $params['user_id'] ) ? $params['user_id'] : get_current_user_id();
478 + if ( $user_id <= 0 ) {
479 + $user_id = - 1;
480 + }
481 + // Get courses that user has learned
482 + /*$lp_user_courses = LP_User_Items_DB::getInstance();
483 + $filter_user_course = new LP_User_Items_Filter();
484 + $filter_user_course->only_fields = array( 'DISTINCT(item_id) AS ID' );
485 + $filter_user_course->user_id = $user_id;
486 + $filter_user_course->item_type = LP_COURSE_CPT;
487 + $filter_user_course->return_string_query = true;
488 + $user_courses_query = $lp_user_courses->get_user_items( $filter_user_course );
489 + $params = $lp_user_courses->get_user_items( $filter_user_course );
490 + $filter->where[] = "AND p.ID IN ({$user_courses_query})";*/
491 +
492 + $lp_user_items_db = LP_User_Items_DB::getInstance();
493 + $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';
494 + $filter->join[] = "LEFT JOIN $lp_user_items_db->tb_lp_user_items AS ui ON p.ID = ui.item_id";
495 + $filter->where[] = $lp_user_items_db->wpdb->prepare( 'AND ui.user_id = %s', $user_id );
496 + $filter->where[] = $lp_user_items_db->wpdb->prepare( 'AND ui.item_type = %s', LP_COURSE_CPT );
497 + if ( ! empty( $params['course_filter'] ) ) {
498 + $filter->where[] = $lp_user_items_db->wpdb->prepare( 'AND ui.graduation = %s', $params['course_filter'] );
499 + }
500 + } else {
501 + // Check cache with case not learned
502 + $courses_cache = $lp_courses_cache->get_cache( $key_cache );
503 + if ( $courses_cache !== false ) {
504 + $courses = $courses_cache;
505 + $total = (int) $lp_courses_cache->get_cache( $key_cache_total );
506 + $total_pages = (int) $lp_courses_cache->get_cache( $key_cache_total_pages );
507 +
508 + $response = rest_ensure_response( $courses );
509 + $response->header( 'X-WP-Total', $total );
510 + $response->header( 'X-WP-TotalPages', $total_pages );
511 +
512 + return $response;
513 + }
514 + }
515 +
516 + $rs_courses = Courses::get_courses( $filter, $total );
517 + $courses = $this->prepare_struct_courses_response( $rs_courses, $params );
518 + $total_pages = LP_Database::get_total_pages( $filter->limit, $total );
519 + } catch ( Throwable $e ) {
520 + $res->message = $e->getMessage();
521 +
522 + return $res;
523 + }
524 +
525 + $response = rest_ensure_response( $courses );
526 + $response->header( 'X-WP-Total', $total );
527 + $response->header( 'X-WP-TotalPages', $total_pages );
528 +
529 + // Set cache with case not learned
530 + if ( empty( $params['learned'] ) ) {
531 + $lp_courses_cache->set_cache( $key_cache, json_encode( $courses, JSON_UNESCAPED_UNICODE ) );
532 + $lp_courses_cache->save_cache_keys( LP_Courses_Cache::KEYS_QUERY_COURSES_APP, $key_cache );
533 + $lp_courses_cache->set_cache( $key_cache_total, $total );
534 + $lp_courses_cache->save_cache_keys( LP_Courses_Cache::KEYS_QUERY_COURSES_APP, $key_cache_total );
535 + $lp_courses_cache->set_cache( $key_cache_total_pages, $total_pages );
536 + $lp_courses_cache->save_cache_keys( LP_Courses_Cache::KEYS_QUERY_COURSES_APP, $key_cache_total_pages );
537 + }
538 +
539 + return $response;
540 + }
541 +
542 + /**
543 + * @throws Exception
544 + */
545 + public function prepare_struct_courses_response( $courses, $params ): array {
546 + $data = [];
547 + foreach ( $courses as $courseObj ) {
548 + $course = CourseModel::find( $courseObj->ID, true );
549 + if ( empty( $course ) ) {
550 + continue;
551 + }
552 +
553 + $courseObjPrepare = new stdClass();
554 + $courseObjPrepare->id = (int) $courseObj->ID ?? 0;
555 + $courseObjPrepare->name = html_entity_decode( $course->get_title() );
556 + $courseObjPrepare->slug = $courseObj->post_name ?? get_post_field( 'post_name', $courseObj->ID );
557 + $courseObjPrepare->permalink = get_permalink( $courseObj->ID );
558 + $courseObjPrepare->excerpt = $courseObj->post_excerpt ?? '';
559 + $courseObjPrepare->count_students = method_exists( $course, 'count_students' ) ? (int) $course->count_students() : 0;
560 + $courseObjPrepare->image = $course->get_image_url();
561 + $author = $course->get_author_model();
562 + $courseObjPrepare->instructor = ! empty( $author ) ? $this->get_author_info( $author ) : [];
563 + $duration = $course->get_meta_value_by_key( CoursePostModel::META_KEY_DURATION, '' );
564 + $duration_arr = explode( ' ', $duration );
565 + $duration_number = floatval( $duration_arr[0] ?? 0 );
566 + $duration_type = $duration_arr[1] ?? '';
567 + if ( empty( $duration_number ) ) {
568 + $duration_str = __( 'Lifetime', 'learnpress' );
569 + } else {
570 + $duration_str = LP_Datetime::get_string_plural_duration( $duration_number, $duration_type );
571 + }
572 + $courseObjPrepare->duration = $duration_str;
573 + $course_categories = $course->get_categories();
574 + // Add key id, for old app.
575 + foreach ( $course_categories as $k => $category ) {
576 + $category->id = $category->term_id;
577 + $course_categories[ $k ] = $category;
578 + }
579 + $courseObjPrepare->categories = $course_categories;
580 + $courseObjPrepare->price = $course->get_price();
581 + $courseObjPrepare->price_rendered = $this->render_course_price( $course );
582 + $courseObjPrepare->origin_price = $course->get_regular_price();
583 + $courseObjPrepare->origin_price_rendered = html_entity_decode(
584 + learn_press_format_price( $courseObjPrepare->origin_price, true )
585 + );
586 + $courseObjPrepare->on_sale = $course->has_sale_price();
587 + $courseObjPrepare->sale_price = (float) $course->get_sale_price();
588 + $courseObjPrepare->sale_price_rendered = html_entity_decode(
589 + learn_press_format_price( $course->get_sale_price(), true )
590 + );
591 + // When release Addon Course Review v4.1.3 a long time, we will remove this code.
592 + $courseObjPrepare->rating = $this->get_course_rating( $courseObj->ID );
593 + // All settings of the course metabox, same as the single course response.
594 + $courseObjPrepare->meta_data = (object) $this->get_course_meta( $courseObj->ID );
595 +
596 + // Add more fields
597 + if ( ! empty( $params['learned'] ) ) {
598 + $courseObjPrepare->course_data = new stdClass();
599 + $courseObjPrepare->course_data->user_item_id = $courseObj->user_item_id ?? 0;
600 + $courseObjPrepare->course_data->user_id = $courseObj->user_id ?? 0;
601 + $courseObjPrepare->course_data->item_id = $courseObj->item_id ?? 0;
602 + $courseObjPrepare->course_data->item_type = $courseObj->item_type ?? 0;
603 + $courseObjPrepare->course_data->status = $courseObj->status ?? '';
604 + $courseObjPrepare->course_data->graduation = $courseObj->graduation ?? '';
605 + $courseObjPrepare->course_data->start_time = $courseObj->start_time ?? '';
606 + $courseObjPrepare->course_data->end_time = $courseObj->end_time ?? '';
607 +
608 + $userCourse = new UserCourseModel( $courseObjPrepare->course_data );
609 + $expirationTime = $userCourse->get_expiration_time();
610 + $courseObjPrepare->course_data->expiration_time = $expirationTime ? $expirationTime->format( LP_Datetime::I18N_FORMAT ) : __( 'Lifetime', 'learnpress' );
611 + $courseObjPrepare->course_data->result = $userCourse->calculate_course_results();
612 + }
613 +
614 + $courseObjPrepare = apply_filters( 'learnPress/prepare_struct_courses_response/courseObjPrepare', $courseObjPrepare, $course );
615 + $data[] = $courseObjPrepare;
616 + }
617 +
618 + return $data;
619 + }
620 +
374 621 public function enroll_course( $request ) {
375 622 if ( ! class_exists( 'LP_REST_Courses_Controller' ) ) {
376 623 include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-courses-controller.php';
377 624 }
@@ -417,15 +664,20 @@
417 664
418 665 public function retake_course( $request ) {
419 666 $response = new LP_REST_Response();
420 667
421 - if ( ! class_exists( 'LP_REST_Courses_Controller' ) ) {
422 - include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-courses-controller.php';
668 + try {
669 + if ( ! class_exists( 'LP_REST_Courses_Controller' ) ) {
670 + include_once LP_PLUGIN_PATH . 'inc/rest-api/v1/frontend/class-lp-rest-courses-controller.php';
671 + }
672 +
673 + $course_controller = new LP_REST_Courses_Controller();
674 + $response = $course_controller->retake_course( $request );
675 + } catch ( Throwable $e ) {
676 + $response->message = $e->getMessage();
423 677 }
424 678
425 - $course_controller = new LP_REST_Courses_Controller();
426 -
427 - return $course_controller->retake_course( $request );
679 + return $response;
428 680 }
429 681
430 682 /**
431 683 * Create a single product.
@@ -430,8 +682,9 @@
430 682 /**
431 683 * Create a single product.
432 684 *
433 685 * @param WP_REST_Request $request Full details about the request.
686 + *
434 687 * @return WP_Error|WP_REST_Response
435 688 */
436 689 public function create_item( $request ) {
437 690 if ( ! empty( $request['id'] ) ) {
@@ -488,8 +741,13 @@
488 741 public function prepare_object_for_response( $object, $request ) {
489 742 $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
490 743 $data = $this->get_course_data( $object, $context, $request );
491 744
745 + // Ensure sections are always included for single-item response, regardless of schema/context filtering.
746 + if ( empty( $request['optimize'] ) && ! isset( $data['sections'] ) ) {
747 + $data['sections'] = $this->get_all_items( $object );
748 + }
749 +
492 750 $response = rest_ensure_response( $data );
493 751
494 752 return apply_filters( "lp_jwt_rest_prepare_{$this->post_type}_object", $response, $object, $request );
495 753 }
@@ -582,9 +840,9 @@
582 840 case 'tags':
583 841 $data['tags'] = $this->get_course_taxonomy( $id, 'course_tag' );
584 842 break;
585 843 case 'instructor':
586 - $data['instructor'] = $this->get_instructor_info( $id, $request, $course );
844 + $data['instructor'] = $this->get_instructor_info( $course );
587 845 break;
588 846 case 'sections':
589 847 $data['sections'] = $this->get_all_items( $course );
590 848 break;
@@ -619,8 +877,34 @@
619 877
620 878 return $data;
621 879 }
622 880
881 + /**
882 + * Handle price course return to App
883 + *
884 + * @param CourseModel $course
885 + *
886 + * @return string
887 + * @throws Exception
888 + * @version 1.0.0
889 + * @since 4.2.6.9
890 + */
891 + public function render_course_price( CourseModel $course ): string {
892 + $price_string = '';
893 +
894 + if ( $course->is_free() ) {
895 + $price_string = apply_filters(
896 + 'learn_press_course_price_html_free',
897 + esc_html__( 'Free', 'learnpress' ),
898 + $this
899 + );
900 + } else {
901 + $price_string .= learn_press_format_price( $course->get_price() );
902 + }
903 +
904 + return html_entity_decode( $price_string );
905 + }
906 +
623 907 public function get_retaken_count( $id ) {
624 908 $user_id = get_current_user_id();
625 909
626 910 if ( ! $user_id ) {
@@ -694,30 +978,89 @@
694 978
695 979 return false;
696 980 }
697 981
698 - public function get_instructor_info( $id, $request, $course ) {
699 - $user_id = get_post_meta( $id, '_lp_course_author', true );
982 + /**
983 + * Get instructor info
984 + *
985 + * @param LP_Course $course
986 + *
987 + * @return array
988 + */
989 + public function get_instructor_info( $course ) {
990 + if ( ! $course ) {
991 + return [];
992 + }
700 993
701 - $output = array();
994 + if ( $course instanceof LP_Course ) {
995 + $courseModel = CourseModel::find( $course->get_id(), true );
996 + } elseif ( $course instanceof CourseModel ) {
997 + $courseModel = $course;
998 + } else {
999 + return [];
1000 + }
702 1001
703 - $extra_info = learn_press_get_user_extra_profile_info( $user_id );
1002 + $output = array();
1003 + $extra_info = learn_press_get_user_extra_profile_info( $courseModel->get_id() );
1004 + $author = $courseModel->get_author_model();
1005 + if ( ! $author ) {
1006 + return [];
1007 + }
704 1008
705 - $instructor = $course->get_instructor();
1009 + $output['avatar'] = $author->get_avatar_url();
1010 + $output['id'] = $author->get_id();
1011 + $output['name'] = $author->get_display_name();
1012 + $output['description'] = $author->get_description();
1013 + $output['social'] = $extra_info;
1014 + $output['statistic'] = $this->get_instructor_statistic( $author );
706 1015
707 - $output['avatar'] = $instructor->get_upload_profile_src();
1016 + return $output;
1017 + }
708 1018
709 - if ( $user_id ) {
710 - $user = get_user_by( 'ID', absint( $user_id ) );
1019 + /**
1020 + * Count the courses and the students of an instructor.
1021 + *
1022 + * Only used for the single course response, it costs several queries and
1023 + * "LP_Cache::cache_load_first" only keeps the result within the current request.
1024 + *
1025 + * @param UserModel $author
1026 + *
1027 + * @return array
1028 + */
1029 + protected function get_instructor_statistic( UserModel $author ): array {
1030 + $statistic = $author->get_instructor_statistic();
711 1031
712 - if ( $user ) {
713 - $output['id'] = absint( $user_id );
714 - $output['name'] = $user->display_name;
715 - $output['description'] = $user->description;
716 - $output['social'] = $extra_info;
717 - }
1032 + // Keep the extra keys added by add-ons, only cast the ones of the core.
1033 + $numeric_keys = array(
1034 + 'total_course',
1035 + 'published_course',
1036 + 'pending_course',
1037 + 'total_student',
1038 + 'student_completed',
1039 + 'student_in_progress',
1040 + );
1041 +
1042 + foreach ( $numeric_keys as $numeric_key ) {
1043 + $statistic[ $numeric_key ] = absint( $statistic[ $numeric_key ] ?? 0 );
718 1044 }
719 1045
1046 + return $statistic;
1047 + }
1048 +
1049 + /**
1050 + * Get instructor info
1051 + *
1052 + * @param UserModel $author
1053 + *
1054 + * @return array
1055 + */
1056 + public function get_author_info( $author ): array {
1057 + $output = [];
1058 + $output['avatar'] = $author->get_avatar_url();
1059 + $output['id'] = $author->get_id();
1060 + $output['name'] = $author->display_name;
1061 + $output['description'] = $author->get_description();
1062 +
720 1063 return $output;
721 1064 }
722 1065
723 1066 public function get_item_learned_ids( $request ) {
@@ -762,11 +1105,11 @@
762 1105
763 1106 return array(
764 1107 'graduation' => $course_data->get_graduation() ?? '',
765 1108 'status' => $course_data->get_status() ?? '',
766 - 'start_time' => $course_data->get_start_time() ? lp_jwt_prepare_date_response( $course_data->get_start_time()->toSql( false ) ) : null,
767 - 'end_time' => $course_data->get_end_time() ? lp_jwt_prepare_date_response( $course_data->get_end_time()->toSql( false ) ) : null,
768 - 'expiration_time' => $course_data->get_expiration_time() ? lp_jwt_prepare_date_response( $course_data->get_expiration_time()->toSql( false ) ) : '',
1109 + 'start_time' => $course_data->get_start_time() ? lp_jwt_prepare_date_response( $course_data->get_start_time()->toSql() ) : null,
1110 + 'end_time' => $course_data->get_end_time() ? lp_jwt_prepare_date_response( $course_data->get_end_time()->toSql() ) : null,
1111 + 'expiration_time' => $course_data->get_expiration_time() ? lp_jwt_prepare_date_response( $course_data->get_expiration_time()->toSql() ) : '',
769 1112 'result' => $course_data->calculate_course_results(),
770 1113 );
771 1114 }
772 1115
@@ -789,8 +1132,10 @@
789 1132
790 1133 /**
791 1134 * Get Items of sections
792 1135 *
1136 + * @param LP_Course $course
1137 + *
793 1138 * @throws Exception
794 1139 * @editor tungnx
795 1140 * @modify 4.1.3
796 1141 * @version 4.0.1
@@ -840,14 +1185,15 @@
840 1185
841 1186 $data_item[] = array(
842 1187 'id' => $item->get_id(),
843 1188 'type' => $item->get_item_type(),
1189 + 'slug' => $post->post_name,
844 1190 'title' => $post->post_title,
845 1191 'preview' => $item->is_preview(),
846 1192 'duration' => $item->get_duration()->to_timer( $format, true ),
847 1193 'graduation' => $user_item ? $user_item->get_graduation() : '',
848 1194 'status' => $user_item ? $user_item->get_status() : '',
849 - 'locked' => isset( $can_view_item->flag ) ? ! $can_view_item->flag : true,
1195 + 'locked' => ! isset( $can_view_item->flag ) || ! $can_view_item->flag,
850 1196 );
851 1197 }
852 1198 }
853 1199
@@ -859,12 +1205,17 @@
859 1205
860 1206 return $output;
861 1207 }
862 1208
1209 + /**
1210 + * Get all settings of the course metabox.
1211 + *
1212 + * @param int $id Course ID.
1213 + *
1214 + * @return array
1215 + */
863 1216 public function get_course_meta( $id ) {
864 - $user_id = get_current_user_id();
865 -
866 - if ( ! $user_id ) {
1217 + if ( empty( absint( $id ) ) ) {
867 1218 return array();
868 1219 }
869 1220
870 1221 if ( ! class_exists( 'LP_Meta_Box' ) ) {
@@ -874,9 +1225,9 @@
874 1225 if ( ! class_exists( 'LP_Meta_Box_Course' ) ) {
875 1226 include_once LP_PLUGIN_PATH . 'inc/admin/views/meta-boxes/course/settings.php';
876 1227 }
877 1228
878 - $metabox = new LP_Meta_Box_Course();
1229 + $metabox = LP_Meta_Box_Course::instance();
879 1230
880 1231 $output = array();
881 1232
882 1233 foreach ( $metabox->metabox( $id ) as $key => $tab ) {
@@ -1229,8 +1580,14 @@
1229 1580 'type' => 'array',
1230 1581 'context' => array( 'view' ),
1231 1582 'readonly' => true,
1232 1583 ),
1584 + 'statistic' => array(
1585 + 'description' => __( 'Total courses and students of the instructor.', 'learnpress' ),
1586 + 'type' => 'object',
1587 + 'context' => array( 'view' ),
1588 + 'readonly' => true,
1589 + ),
1233 1590 ),
1234 1591 ),
1235 1592 ),
1236 1593 'sections' => array(
@@ -1271,8 +1628,13 @@
1271 1628 'context' => array( 'view', 'edit' ),
1272 1629 ),
1273 1630 'type' => array(
1274 1631 'description' => __( 'Item Type.', 'learnpress' ),
1632 + 'type' => 'string',
1633 + 'context' => array( 'view', 'edit' ),
1634 + ),
1635 + 'slug' => array(
1636 + 'description' => __( 'Item slug.', 'learnpress' ),
1275 1637 'type' => 'string',
1276 1638 'context' => array( 'view', 'edit' ),
1277 1639 ),
1278 1640 'title' => array(