PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.7.2
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.7.2
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 / rest-api / v1 / frontend / class-lp-rest-courses-controller.php

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

708 lines 21.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class LP_REST_Courses_Controller
5 */
6 class LP_REST_Courses_Controller extends LP_Abstract_REST_Controller {
7 /**
8 * LP_REST_Courses_Controller constructor.
9 */
10 public function __construct() {
11 $this->namespace = 'lp/v1';
12 $this->rest_base = 'courses';
13 parent::__construct();
14 }
15
16 /**
17 * Register routes API
18 */
19 public function register_routes() {
20 $this->routes = array(
21 'purchase-course' => array(
22 array(
23 'methods' => WP_REST_Server::CREATABLE,
24 'callback' => array( $this, 'purchase_course' ),
25 'permission_callback' => '__return_true',
26 ),
27 ),
28 'enroll-course' => array(
29 array(
30 'methods' => WP_REST_Server::CREATABLE,
31 'callback' => array( $this, 'enroll_courses' ),
32 'permission_callback' => '__return_true',
33 ),
34 ),
35 'retake-course' => array(
36 array(
37 'methods' => WP_REST_Server::CREATABLE,
38 'callback' => array( $this, 'retake_course' ),
39 'permission_callback' => function () {
40 return is_user_logged_in();
41 },
42 'schema' => array(
43 'type' => 'int',
44 ),
45 ),
46 ),
47 'archive-course' => array(
48 array(
49 'methods' => WP_REST_Server::ALLMETHODS,
50 'callback' => array( $this, 'list_courses' ),
51 'permission_callback' => '__return_true',
52 'args' => [],
53 ),
54 ),
55 '(?P<key>[\w]+)' => array(
56 'args' => array(
57 'id' => array(
58 'description' => __( 'Unique identifier for the resource.', 'learnpress' ),
59 'type' => 'string',
60 ),
61 ),
62 array(
63 'methods' => WP_REST_Server::READABLE,
64 'callback' => array( $this, 'get_item' ),
65 'permission_callback' => array( $this, 'check_admin_permission' ),
66 ),
67 array(
68 'methods' => WP_REST_Server::EDITABLE,
69 'callback' => array( $this, 'update_item' ),
70 'permission_callback' => array( $this, 'check_admin_permission' ),
71 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
72 ),
73 array(
74 'methods' => WP_REST_Server::DELETABLE,
75 'callback' => array( $this, 'delete_item' ),
76 'permission_callback' => array( $this, 'check_admin_permission' ),
77 ),
78 'schema' => array( $this, 'get_public_item_schema' ),
79 ),
80 'continue-course' => array(
81 array(
82 'methods' => WP_REST_Server::CREATABLE,
83 'callback' => array( $this, 'continue_course' ),
84 'permission_callback' => function () {
85 return is_user_logged_in();
86 },
87 ),
88 ),
89 );
90
91 parent::register_routes();
92 }
93
94 /**
95 * Check user is Admin
96 *
97 * @return bool
98 */
99 public function check_admin_permission(): bool {
100 return LP_Abstract_API::check_admin_permission();
101 }
102
103 /**
104 * Get list courses
105 *
106 * @param WP_REST_Request $request
107 *
108 * @return LP_REST_Response
109 */
110 public function list_courses( WP_REST_Request $request ): LP_REST_Response {
111 $response = new LP_REST_Response();
112 $response->data = new stdClass();
113
114 try {
115 $filter = new LP_Course_Filter();
116 $filter->page = absint( $request['paged'] ?? 1 );
117 $filter->post_title = LP_Helper::sanitize_params_submitted( $request['c_search'] ?? '' );
118 $fields_str = LP_Helper::sanitize_params_submitted( urldecode( $request['c_fields'] ?? '' ) );
119 $fields_exclude_str = LP_Helper::sanitize_params_submitted( urldecode( $request['c_exclude_fields'] ?? '' ) );
120 if ( ! empty( $fields_str ) ) {
121 $fields = explode( ',', $fields_str );
122 $filter->fields = $fields;
123 }
124 if ( ! empty( $fields_exclude_str ) ) {
125 $fields_exclude = explode( ',', $fields_exclude_str );
126 $filter->exclude_fields = $fields_exclude;
127 }
128 $filter->post_author = LP_Helper::sanitize_params_submitted( $request['c_author'] ?? 0 );
129 $author_ids_str = LP_Helper::sanitize_params_submitted( $request['c_authors'] ?? 0 );
130 if ( ! empty( $author_ids_str ) ) {
131 $author_ids = explode( ',', $author_ids_str );
132 $filter->post_authors = $author_ids;
133 }
134 $term_ids_str = LP_Helper::sanitize_params_submitted( urldecode( $request['term_id'] ) ?? '' );
135 if ( ! empty( $term_ids_str ) ) {
136 $term_ids = explode( ',', $term_ids_str );
137 $filter->term_ids = $term_ids;
138 }
139
140 $on_sale = absint( $request['on_sale'] ?? '0' );
141 1 === $on_sale ? $filter->sort_by[] = 'on_sale' : '';
142 $on_feature = absint( $request['on_feature'] ?? '0' );
143 1 === $on_feature ? $filter->sort_by[] = 'on_feature' : '';
144
145 $filter->order_by = LP_Helper::sanitize_params_submitted( ! empty( $request['order_by'] ) ? $request['order_by'] : 'post_date' );
146 $filter->order = LP_Helper::sanitize_params_submitted( ! empty( $request['order'] ) ? $request['order'] : 'DESC' );
147 $filter->limit = $request['limit'] ?? LP_Settings::get_option( 'archive_course_limit', 10 );
148 $return_type = $request['return_type'] ?? 'html';
149 if ( 'json' !== $return_type ) {
150 $filter->only_fields = array( 'DISTINCT(ID) AS ID' );
151 }
152
153 $total_rows = 0;
154 $filter = apply_filters( 'lp/api/courses/filter', $filter, $request );
155 $courses = LP_Course::get_courses( $filter, $total_rows );
156 $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
157
158 if ( 'json' === $return_type ) {
159 $response->data->courses = $courses;
160 $response->data->total_pages = $total_pages;
161 } else {
162 // For return data has html
163 if ( $courses ) {
164 global $wp, $post;
165
166 // Pagination
167 $archive_link = get_post_type_archive_link( LP_COURSE_CPT );
168
169 if ( isset( $term_link ) && ! is_wp_error( $term_link ) ) {
170 $archive_link = $term_link;
171 }
172
173 $template_pagination_path = $request['template_pagination_path'] ?? '';
174 if ( ! isset( $request['no_pagination'] ) ) {
175 if ( ! empty( $template_pagination_path ) ) {
176 $response->data->pagination = include $template_pagination_path;
177 } else {
178 $response->data->pagination = learn_press_get_template_content(
179 'loop/course/pagination.php',
180 array(
181 'total' => $total_pages,
182 'paged' => $filter->page,
183 )
184 );
185 }
186 }
187 // End Pagination
188
189 // Content items
190 ob_start();
191 $template_path_item = urldecode( $request['template_path_item'] ?? '' );
192 $template_path = urldecode( $request['template_path'] ?? '' ); // For wrapper all items, no foreach
193 $args_custom = json_decode( wp_unslash( $request['args_custom'] ?? '' ), true );
194
195 // For custom template return all list courses no foreach
196 if ( ! empty( $template_path ) ) {
197 if ( is_array( $args_custom ) && ! empty( $args_custom ) ) {
198 extract( $args_custom );
199 }
200
201 if ( file_exists( $template_path ) ) {
202 include $template_path;
203 }
204 } else {
205 // For custom template return all list courses foreach
206 if ( ! empty( $template_path_item ) ) {
207 if ( isset( $request['args_custom'] ) ) {
208 $args_custom = json_decode( $request['args_custom'], true );
209 }
210
211 $template_path_item = urldecode( $template_path_item );
212 }
213
214 // Todo: tungnx - should rewrite call template
215 foreach ( $courses as $course ) {
216 $post = get_post( $course->ID );
217 setup_postdata( $post );
218
219 if ( ! empty( $template_path_item ) ) {
220 if ( $args_custom ) {
221 extract( $args_custom );
222 }
223
224 if ( file_exists( $template_path_item ) ) {
225 include $template_path_item;
226 }
227 } else {
228 learn_press_get_template_part( 'content', 'course' );
229 }
230 }
231
232 wp_reset_postdata();
233 }
234 // End content items
235 } else {
236 LearnPress::instance()->template( 'course' )->no_courses_found();
237 }
238
239 $response->data->content = ob_get_clean();
240 $response->data->totals = $total_rows;
241
242 $from = 1 + ( $filter->page - 1 ) * $filter->limit;
243 $to = ( $filter->page * $filter->limit > $total_rows ) ? $total_rows : $filter->page * $filter->limit;
244
245 if ( 0 === $total_rows ) {
246 $response->data->from_to = '';
247 } elseif ( 1 === $total_rows ) {
248 $response->data->from_to = esc_html__( 'Showing only one result', 'learnpress' );
249 } else {
250 if ( $from == $to ) {
251 $response->data->from_to = sprintf( esc_html__( 'Showing last course of %s results', 'learnpress' ), $total_rows );
252 } else {
253 $from_to = $from . '-' . $to;
254 $response->data->from_to = sprintf( esc_html__( 'Showing %1$s of %2$s results', 'learnpress' ), $from_to, $total_rows );
255 }
256 }
257 }
258
259 $response->status = 'success';
260 } catch ( Throwable $e ) {
261 $response->message = $e->getMessage();
262 }
263
264 return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
265 }
266
267 /**
268 * Rest API for Enroll in single course.
269 *
270 * @param WP_REST_Request $request
271 *
272 * @throws Exception .
273 * @author Nhamdv
274 * @editor tungnx
275 * @version 1.0.1
276 * @since 4.0.0
277 * @modify 4.1.2
278 */
279 public function enroll_courses( WP_REST_Request $request ) {
280 $response = new LP_REST_Response();
281 $response->data = new stdClass();
282 $lp_user_items_db = LP_User_Items_DB::getInstance();
283
284 try {
285 if ( empty( absint( $request['id'] ) ) ) {
286 throw new Exception( esc_html__( 'Error: No course available!.', 'learnpress' ) );
287 }
288
289 $course_id = absint( $request['id'] );
290 $course = learn_press_get_course( $course_id );
291 $user = learn_press_get_current_user();
292
293 if ( ! $course ) {
294 throw new Exception( esc_html__( 'Invalid course!', 'learnpress' ) );
295 }
296
297 $can_enroll = $user->can_enroll_course( $course_id, false );
298
299 if ( ! $can_enroll->check ) {
300 throw new Exception( $can_enroll->message ?? esc_html__( 'Error: Cannot enroll course.', 'learnpress' ) );
301 }
302
303 $filter = new LP_User_Items_Filter();
304 $filter->user_id = get_current_user_id();
305 $filter->item_id = $course_id;
306 $course_item = $lp_user_items_db->get_last_user_course( $filter );
307
308 // Case: if user bought course - or create order manual with order "completed".
309 if ( $course_item && 'purchased' == $course_item->status ) {
310 $user_item_data = [
311 'user_item_id' => $course_item->user_item_id,
312 'graduation' => LP_COURSE_GRADUATION_IN_PROGRESS,
313 'status' => LP_COURSE_ENROLLED,
314 'start_time' => current_time( 'mysql', true ),
315 ];
316
317 $user_item_new_or_update = new LP_User_Item_Course( $user_item_data );
318 $result = $user_item_new_or_update->update();
319
320 if ( ! $result ) {
321 throw new Exception( esc_html__( 'Error: Can\'t Enroll course.', 'learnpress' ) );
322 }
323
324 do_action( 'learnpress/user/course-enrolled', $course_item->ref_id, $course_id, $user->get_id() );
325 } else { // Case enroll course free
326 LearnPress::instance()->session->set( 'order_awaiting_payment', '' );
327
328 $cart = LearnPress::instance()->cart;
329 $checkout = LP_Checkout::instance();
330
331 if ( ! learn_press_enable_cart() ) {
332 $order_awaiting_payment = LearnPress::instance()->session->order_awaiting_payment;
333 $cart->empty_cart();
334 LearnPress::instance()->session->order_awaiting_payment = $order_awaiting_payment;
335 }
336
337 $cart_id = $cart->add_to_cart( $course_id, 1, array() );
338
339 if ( ! $cart_id ) {
340 throw new Exception( esc_html__( 'Error: Can\'t add Course to cart.', 'learnpress' ) );
341 }
342
343 if ( is_user_logged_in() ) {
344 $order_id = $checkout->create_order();
345
346 if ( is_wp_error( $order_id ) ) {
347 throw new Exception( $order_id->get_error_message() );
348 }
349
350 $order = new LP_Order( $order_id );
351
352 $order->payment_complete();
353
354 $cart->empty_cart();
355 }
356 }
357
358 if ( is_user_logged_in() ) {
359 $response->status = 'success';
360 // Course has no items
361 $response->message = esc_html__(
362 'Congrats! You enroll course successfully. Redirecting...',
363 'learnpress'
364 );
365
366 $response->data->redirect = $course->get_redirect_url_after_enroll();
367
368 if ( empty( $course->count_items() ) ) {
369 $response->data->redirect = get_permalink( $course->get_id() );
370 }
371 } else {
372 $redirect_url = apply_filters(
373 'learnpress/rest-api/courses/enroll/redirect',
374 learn_press_get_page_link( 'checkout' ),
375 $course_id
376 );
377
378 if ( empty( $redirect_url ) ) {
379 throw new Exception( __( 'Error: Please setup page for checkout.', 'learnpress' ) );
380 } elseif ( ! is_user_logged_in() ) { // Fix case: cache page with user anonymous
381 $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
382 }
383
384 $response->message = esc_html__( 'Redirecting...', 'learnpress' );
385 $response->data->redirect = $redirect_url;
386 }
387 } catch ( Exception $e ) {
388 $response->message = $e->getMessage();
389 }
390
391 wp_send_json( $response );
392 }
393
394 /**
395 * Rest API for Purchase course in single course.
396 *
397 * @param WP_REST_Request $request .
398 *
399 * @return WP_REST_Response|WP_Error
400 * @throws Exception .
401 * @author Nhamdv
402 */
403 public function purchase_course( WP_REST_Request $request ) {
404 $response = new LP_REST_Response();
405 $response->data = new stdClass();
406 $params = $request->get_params();
407 $lp_user_items_db = LP_User_Items_DB::getInstance();
408
409 try {
410 $course_id = $params['id'];
411 $allow_repurchase_type = $params['repurchaseType'] ?? false;
412
413 if ( ! $course_id ) {
414 throw new Exception( __( 'Error: Invalid Course ID.', 'learnpress' ) );
415 }
416
417 $course = learn_press_get_course( $course_id );
418
419 if ( ! $course ) {
420 throw new Exception( __( 'Error: No Course available.', 'learnpress' ) );
421 }
422
423 $user = learn_press_get_current_user();
424
425 if ( ! $user->can_purchase_course( $course_id ) ) {
426 throw new Exception( esc_html__( 'Error: Cannot purchase course!.', 'learnpress' ) );
427 }
428
429 $latest_user_item_id = 0;
430
431 $filter = new LP_User_Items_Filter();
432 $filter->user_id = get_current_user_id();
433 $filter->item_id = $course_id;
434 $course_item = $lp_user_items_db->get_last_user_course( $filter );
435
436 if ( $course_item && isset( $course_item->user_item_id ) ) {
437 $latest_user_item_id = $course_item->user_item_id;
438 }
439
440 if ( $course->allow_repurchase() && ! empty( $latest_user_item_id ) && empty( $allow_repurchase_type ) ) {
441 if ( $course->allow_repurchase_course_option() === 'popup' ) {
442 ob_start();
443 ?>
444 <div class="lp_allow_repuchase_select">
445 <ul>
446 <li>
447 <label>
448 <input name="_lp_allow_repurchase_type" value="reset" type="radio" checked="checked" />
449 <?php esc_html_e( 'Reset Course progress', 'learnpress' ); ?>
450 </label>
451 </li>
452 <li>
453 <label>
454 <input name="_lp_allow_repurchase_type" value="keep" type="radio" />
455 <?php esc_html_e( 'Continue Course progress', 'learnpress' ); ?>
456 </label>
457 </li>
458 </ul>
459 </div>
460 <?php
461 $response->data->html = ob_get_clean();
462 $response->data->type = 'allow_repurchase';
463 $response->data->titlePopup = esc_html__( 'Repurchase Options', 'learnpress' );
464 $response->status = 'success';
465
466 return rest_ensure_response( $response );
467 } else {
468 learn_press_update_user_item_meta( $latest_user_item_id, '_lp_allow_repurchase_type', $course->allow_repurchase_course_option() );
469 }
470 }
471
472 LearnPress::instance()->session->set( 'order_awaiting_payment', '' );
473
474 $cart = LearnPress::instance()->cart;
475 $checkout = LP_Checkout::instance();
476
477 if ( ! learn_press_enable_cart() ) {
478 $order_awaiting_payment = LearnPress::instance()->session->order_awaiting_payment;
479 $cart->empty_cart();
480 LearnPress::instance()->session->order_awaiting_payment = $order_awaiting_payment;
481 }
482
483 do_action( 'learnpress/rest-api/courses/purchase/before-add-to-cart' );
484
485 $cart_id = $cart->add_to_cart( $course_id, 1, array() );
486
487 if ( ! $cart_id ) {
488 throw new Exception( __( 'Error: Can\'t add Course to cart.', 'learnpress' ) );
489 }
490
491 if ( ! empty( $allow_repurchase_type ) ) {
492 learn_press_update_user_item_meta( $latest_user_item_id, '_lp_allow_repurchase_type', $allow_repurchase_type );
493 }
494
495 $redirect_url = apply_filters(
496 'learnpress/rest-api/courses/purchase/redirect',
497 learn_press_get_page_link( 'checkout' ),
498 $course_id,
499 $cart_id
500 );
501
502 if ( empty( $redirect_url ) ) {
503 throw new Exception( __( 'Error: Please setup page for checkout.', 'learnpress' ) );
504 } elseif ( ! is_user_logged_in() ) { // Fix case: cache page with user anonymous
505 $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
506 }
507
508 $response->status = 'success';
509 $response->message = sprintf(
510 esc_html__( '"%s" has been added to your cart.', 'learnpress' ),
511 $course->get_title()
512 );
513 $response->data->redirect = $redirect_url;
514 } catch ( Exception $e ) {
515 $response->message = $e->getMessage();
516 }
517
518 return rest_ensure_response( $response );
519 }
520
521 /**
522 * Rest API for retake course.
523 *
524 * @param WP_REST_Request $request .
525 *
526 * @throws Exception .
527 */
528 public function retake_course( WP_REST_Request $request ) {
529 $response = new LP_REST_Response();
530
531 try {
532 $course_id = $request->get_param( 'id' );
533
534 if ( ! $course_id ) {
535 throw new Exception( __( 'Invalid params', 'learnpress' ) );
536 }
537
538 $course = learn_press_get_course( $course_id );
539
540 if ( ! $course ) {
541 throw new Exception( __( 'Invalid course', 'learnpress' ) );
542 }
543
544 $user = learn_press_get_current_user();
545
546 // if ( ! is_user_logged_in() ) {
547 // throw new Exception( esc_html__( 'Please login!', 'learnpress' ) );
548 // }
549
550 $can_retry = $user->can_retry_course( $course_id );
551
552 if ( ! $can_retry ) {
553 throw new Exception( __( 'You can\'t retry course', 'learnpress' ) );
554 }
555
556 $user_course_data = $user->get_course_data( $course_id );
557
558 // Up retaken.
559 $user_course_data->increase_retake_count();
560
561 // Set status, start_time, end_time of course to enrolled.
562 $user_course_data->set_status( LP_COURSE_ENROLLED )
563 ->set_start_time( current_time( 'mysql', true ) )
564 ->set_end_time( '' )
565 ->set_graduation( 'in-progress' )
566 ->update();
567
568 // Remove items' course user learned.
569 $filter_remove = new LP_User_Items_Filter();
570 $filter_remove->parent_id = $user_course_data->get_user_item_id();
571 $filter_remove->user_id = $user_course_data->get_user_id();
572 $filter_remove->limit = - 1;
573 LP_User_Items_DB::getInstance()->remove_items_of_user_course( $filter_remove );
574
575 // Create new result in table learnpress_user_item_results.
576 LP_User_Items_Result_DB::instance()->insert( $user_course_data->get_user_item_id() );
577
578 $response->status = 'success';
579 $response->message = esc_html__( 'Now you can learn this course', 'learnpress' );
580 $response->data->url_redirect = $course->get_redirect_url_after_enroll();
581 } catch ( Exception $e ) {
582 $response->message = $e->getMessage();
583 }
584
585 wp_send_json( $response );
586 }
587
588 /**
589 * @param WP_REST_Request $request
590 *
591 * @return WP_REST_Response
592 */
593 public function get_items( $request ) {
594 $settings = LP_Settings::instance();
595 $response = array(
596 'result' => $settings->get(),
597 );
598
599 return rest_ensure_response( $response );
600 }
601
602 /**
603 * @param WP_REST_Request $request
604 *
605 * @return WP_REST_Response
606 */
607 public function get_item( $request ) {
608 $settings = LP_Settings::instance();
609 $response = array(
610 'result' => $settings->get( $request['key'] ),
611 );
612
613 return rest_ensure_response( $response );
614 }
615
616 /**
617 * @param WP_REST_Request $request
618 *
619 * @return WP_REST_Response
620 */
621 public function update_item( $request ) {
622 $response = array();
623 $settings = LP_Settings::instance();
624 $option = $settings->get( $request['key'] );
625
626 $settings->update( $request['key'], $request['data'] );
627 $new_option = $settings->get( $request['key'] );
628 $success = maybe_serialize( $option ) !== maybe_serialize( $new_option );
629
630 $response['success'] = $success;
631 $response['result'] = $success ? $new_option : $option;
632
633 return rest_ensure_response( $response );
634 }
635
636 /**
637 * @param WP_REST_Request $request
638 *
639 * @return WP_REST_Response
640 */
641 public function delete_item( $request ) {
642 $response = array();
643
644 return rest_ensure_response( $response );
645 }
646
647 /**
648 * Rest API for Continue in single course.
649 *
650 * @param WP_REST_Request $request
651 * @author minhpd
652 * @editor tungnx
653 * @since 4.1.4
654 * @version 1.0.2
655 */
656 public function continue_course( WP_REST_Request $request ) {
657 $params = $request->get_params();
658 $response = new LP_REST_Response();
659 $response->data = '';
660
661 try {
662 $flag_found = false;
663 $item_link = '';
664 $course_id = absint( $params['courseId'] ?? 0 );
665 $user_id = absint( $params['userId'] ?? 0 );
666
667 $user = learn_press_get_user( $user_id );
668 $course = learn_press_get_course( $course_id );
669
670 if ( ! $course ) {
671 throw new Exception( __( 'Invalid course', 'learnpress' ) );
672 }
673
674 $sections_items = $course->get_full_sections_and_items_course();
675 $total_items = $course->count_items();
676
677 if ( ! empty( $total_items ) ) {
678 foreach ( $sections_items as $section_items ) {
679 if ( $flag_found ) {
680 break;
681 }
682
683 foreach ( $section_items->items as $item ) {
684 if ( ! $user->has_completed_item( $item->id, $course_id ) ) {
685 $item_link = $course->get_item_link( $item->id );
686 $flag_found = true;
687 break;
688 }
689 }
690 }
691
692 if ( ! $flag_found ) {
693 $item_link = $course->get_item_link( $course->get_first_item_id() );
694 }
695 }
696
697 $response->data = $item_link;
698 $response->status = 'success';
699 $response->message = '';
700 } catch ( Exception $e ) {
701 $response->message = $e->getMessage();
702 }
703
704 wp_send_json( $response );
705 }
706
707 }
708