PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
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
← All changes | inc/rest-api/v1/frontend/class-lp-rest-courses-controller.php +893 -893 4.3.9.1 → 4.4.9 View file →
@@ -1,893 +1,893 @@
1 -<?php
2 -
3 -/**
4 - * Class LP_REST_Courses_Controller
5 - */
6 -
7 -use LearnPress\Background\LPBackgroundAjax;
8 -use LearnPress\ExternalPlugin\Elementor\Widgets\Course\ListCoursesByPageElementor;
9 -use LearnPress\Filters\Course\CourseJsonFilter;
10 -use LearnPress\Helpers\Response;
11 -use LearnPress\Helpers\Template;
12 -use LearnPress\Models\CourseModel;
13 -use LearnPress\Models\PostModel;
14 -use LearnPress\Models\UserItems\UserCourseModel;
15 -use LearnPress\Models\UserItems\UserItemModel;
16 -use LearnPress\Models\UserModel;
17 -use LearnPress\Services\CourseService;
18 -use LearnPress\TemplateHooks\Course\ListCoursesTemplate;
19 -use LearnPress\TemplateHooks\Course\SingleCourseTemplate;
20 -
21 -class LP_REST_Courses_Controller extends LP_Abstract_REST_Controller {
22 - /**
23 - * LP_REST_Courses_Controller constructor.
24 - */
25 - public function __construct() {
26 - $this->namespace = 'lp/v1';
27 - $this->rest_base = 'courses';
28 - parent::__construct();
29 - }
30 -
31 - /**
32 - * Register routes API
33 - */
34 - public function register_routes() {
35 - $this->routes = array(
36 - '' => array(
37 - array(
38 - 'methods' => WP_REST_Server::ALLMETHODS,
39 - 'callback' => array( $this, 'get_courses' ),
40 - ),
41 - ),
42 - 'edit-archive-block' => array(
43 - array(
44 - 'methods' => WP_REST_Server::ALLMETHODS,
45 - 'callback' => array( $this, 'get_courses_archive_block' ),
46 - 'permission_callback' => '__return_true',
47 - 'args' => array(),
48 - ),
49 - ),
50 - 'courses-suggest' => array(
51 - array(
52 - 'methods' => WP_REST_Server::ALLMETHODS,
53 - 'callback' => array( $this, 'courses_suggest' ),
54 - 'permission_callback' => '__return_true',
55 - 'args' => [],
56 - ),
57 - ),
58 - 'purchase-course' => array(
59 - array(
60 - 'methods' => WP_REST_Server::CREATABLE,
61 - 'callback' => array( $this, 'purchase_course' ),
62 - 'permission_callback' => '__return_true',
63 - ),
64 - ),
65 - 'enroll-course' => array(
66 - array(
67 - 'methods' => WP_REST_Server::CREATABLE,
68 - 'callback' => array( $this, 'enroll_courses' ),
69 - 'permission_callback' => '__return_true',
70 - ),
71 - ),
72 - 'retake-course' => array(
73 - array(
74 - 'methods' => WP_REST_Server::CREATABLE,
75 - 'callback' => array( $this, 'retake_course' ),
76 - 'permission_callback' => function () {
77 - return is_user_logged_in();
78 - },
79 - 'schema' => array(
80 - 'type' => 'int',
81 - ),
82 - ),
83 - ),
84 - 'archive-course' => array(
85 - array(
86 - 'methods' => WP_REST_Server::ALLMETHODS,
87 - 'callback' => array( $this, 'list_courses' ),
88 - 'permission_callback' => '__return_true',
89 - 'args' => [],
90 - ),
91 - ),
92 - 'courses-widget-by-page' => array(
93 - array(
94 - 'methods' => WP_REST_Server::ALLMETHODS,
95 - 'callback' => array( $this, 'courses_widget_by_page' ),
96 - 'permission_callback' => '__return_true',
97 - 'args' => [],
98 - ),
99 - ),
100 - '(?P<key>[\w]+)' => array(
101 - 'args' => array(
102 - 'id' => array(
103 - 'description' => __( 'A unique identifier for the resource.', 'learnpress' ),
104 - 'type' => 'string',
105 - ),
106 - ),
107 - array(
108 - 'methods' => WP_REST_Server::READABLE,
109 - 'callback' => array( $this, 'get_item' ),
110 - 'permission_callback' => array( $this, 'check_admin_permission' ),
111 - ),
112 - array(
113 - 'methods' => WP_REST_Server::EDITABLE,
114 - 'callback' => array( $this, 'update_item' ),
115 - 'permission_callback' => array( $this, 'check_admin_permission' ),
116 - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
117 - ),
118 - array(
119 - 'methods' => WP_REST_Server::DELETABLE,
120 - 'callback' => array( $this, 'delete_item' ),
121 - 'permission_callback' => array( $this, 'check_admin_permission' ),
122 - ),
123 - 'schema' => array( $this, 'get_public_item_schema' ),
124 - ),
125 - 'continue-course' => array(
126 - array(
127 - 'methods' => WP_REST_Server::CREATABLE,
128 - 'callback' => array( $this, 'continue_course' ),
129 - 'permission_callback' => '__return_true',
130 - ),
131 - ),
132 - );
133 -
134 - parent::register_routes();
135 - }
136 -
137 - /**
138 - * Check user is Admin
139 - *
140 - * @return bool
141 - */
142 - public function check_admin_permission(): bool {
143 - return LP_Abstract_API::check_admin_permission();
144 - }
145 -
146 - /**
147 - * Get list courses, return JSON data, not to handle HTML
148 - *
149 - * @param WP_REST_Request $request
150 - *
151 - * @return Response
152 - * @since 4.2.8.2
153 - * @version 1.0.1
154 - */
155 - public function get_courses( WP_REST_Request $request ): Response {
156 - $response = new Response();
157 -
158 - try {
159 - $filter = new CourseJsonFilter();
160 - $params = $request->get_params();
161 - $total_rows = 0;
162 -
163 - CourseService::handle_params_for_query_list_courses( $filter, $params );
164 - $filter->only_fields = [ CourseJsonFilter::COL_ID ];
165 -
166 - $coursesRs = CourseService::get_list_courses( $filter, $total_rows );
167 -
168 - $courses = [];
169 - foreach ( $coursesRs as $course ) {
170 - $courseModel = CourseModel::find( $course->ID, true );
171 - $allow_fields = apply_filters(
172 - 'lp/rest-api/courses/allow_fields',
173 - [
174 - CourseJsonFilter::COL_ID,
175 - CourseJsonFilter::COL_POST_TITLE,
176 - CourseJsonFilter::COL_POST_NAME,
177 - CourseJsonFilter::COL_POST_AUTHOR,
178 - CourseJsonFilter::COL_LANG,
179 - CourseJsonFilter::COL_IS_SALE,
180 - ],
181 - $courseModel,
182 - $params
183 - );
184 - if ( ! $courseModel ) {
185 - continue;
186 - }
187 -
188 - $course_info = new stdClass();
189 - foreach ( $allow_fields as $field ) {
190 - $course_info->$field = $courseModel->$field;
191 - }
192 -
193 - $courses[] = $course_info;
194 - }
195 -
196 - $response->status = Response::STATUS_SUCCESS;
197 - $response->data->courses = $courses;
198 - $response->data->total = $total_rows;
199 - $response->data->page = $filter->page;
200 - $response->data->total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
201 - } catch ( Throwable $e ) {
202 - $response->message = $e->getMessage();
203 - }
204 -
205 - return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
206 - }
207 -
208 - /**
209 - * Get list courses display layout when edit on archive block
210 - *
211 - * @param WP_REST_Request $request
212 - *
213 - * @return Response
214 - * @since 4.3.7
215 - * @version 1.0.0
216 - */
217 - public function get_courses_archive_block( WP_REST_Request $request ): Response {
218 - $response = new Response();
219 -
220 - try {
221 - $filter = new CourseJsonFilter();
222 - $params = $request->get_params();
223 - $total_rows = 0;
224 -
225 - CourseService::handle_params_for_query_list_courses( $filter, $params );
226 - // For permission, it only displays for Editor easy config layout, not for get data primary
227 - $filter->only_fields = [ CourseJsonFilter::COL_ID ];
228 - $filter->post_status = [ PostModel::STATUS_PUBLISH ];
229 - $coursesRs = CourseService::get_list_courses( $filter, $total_rows );
230 -
231 - $courses = [];
232 - $singleCourseTemplate = SingleCourseTemplate::instance();
233 - foreach ( $coursesRs as $course ) {
234 - $courseModel = CourseModel::find( $course->ID, true );
235 - if ( ! $courseModel ) {
236 - continue;
237 - }
238 -
239 - $courseItem = new stdClass();
240 - $courseItem->ID = $course->ID;
241 - $courseItem->description = $singleCourseTemplate->html_short_description( $courseModel );
242 - $courseItem->price = $singleCourseTemplate->html_price( $courseModel );
243 - $courseItem->title = $courseModel->get_title();
244 - $courseItem->student = $singleCourseTemplate->html_count_student( $courseModel );
245 - $courseItem->lesson = $singleCourseTemplate->html_count_item( $courseModel, LP_LESSON_CPT );
246 - $courseItem->duration = $singleCourseTemplate->html_duration( $courseModel );
247 - $courseItem->quiz = $singleCourseTemplate->html_count_item( $courseModel, LP_QUIZ_CPT );
248 - $courseItem->level = $singleCourseTemplate->html_level( $courseModel );
249 - $courseItem->image = $singleCourseTemplate->html_image( $courseModel );
250 - $courseItem->instructor = $singleCourseTemplate->html_instructor( $courseModel, false, [ 'is_link' => false ] );
251 - $courseItem->category = $singleCourseTemplate->html_categories( $courseModel );
252 - $courseItem->button = __( 'Read more', 'learnpress' );
253 -
254 - $courses[] = apply_filters( 'lp/rest-api/frontend/course/archive_course/courses', $courseItem, $courseModel );
255 - }
256 -
257 - $response->status = Response::STATUS_SUCCESS;
258 - $response->data->courses = $courses;
259 - $response->data->total = $total_rows;
260 - $response->data->page = $filter->page;
261 - $response->data->total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
262 - } catch ( Throwable $e ) {
263 - $response->message = $e->getMessage();
264 - }
265 -
266 - return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
267 - }
268 -
269 - /**
270 - * Get list courses for search suggest
271 - *
272 - * @param WP_REST_Request $request
273 - *
274 - * @return Response
275 - * @since 4.3.7
276 - * @version 1.0.0
277 - */
278 - public function courses_suggest( WP_REST_Request $request ): Response {
279 - $response = new Response();
280 -
281 - try {
282 - $filter = new CourseJsonFilter();
283 - $params = $request->get_params();
284 - $total_rows = 0;
285 -
286 - CourseService::handle_params_for_query_list_courses( $filter, $params );
287 - $filter->only_fields = [ CourseJsonFilter::COL_ID ];
288 - $filter->post_status = [ PostModel::STATUS_PUBLISH ];
289 -
290 - $coursesRs = CourseService::get_list_courses( $filter, $total_rows );
291 -
292 - $courses = [];
293 - foreach ( $coursesRs as $course ) {
294 - $courseModel = CourseModel::find( $course->ID, true );
295 - $courses[] = $courseModel;
296 - }
297 -
298 - $data = array(
299 - 'courses' => $courses,
300 - 'keyword' => $request['c_search'],
301 - 'total_course' => $total_rows,
302 - );
303 - ob_start();
304 - do_action( 'learn-press/rest-api/courses/suggest/layout', $data );
305 - $response->data->content = ob_get_clean();
306 - $response->data->totals = $total_rows;
307 - $response->status = Response::STATUS_SUCCESS;
308 - } catch ( Throwable $e ) {
309 - $response->message = $e->getMessage();
310 - }
311 -
312 - return apply_filters( 'lp/rest-api/courses-search-suggest/response', $response );
313 - }
314 -
315 - /**
316 - * Get list courses
317 - *
318 - * @param WP_REST_Request $request
319 - *
320 - * @depreacted 4.3.7
321 - * @return LP_REST_Response
322 - */
323 - public function list_courses( WP_REST_Request $request ): LP_REST_Response {
324 - $response = new LP_REST_Response();
325 - $response->data = new stdClass();
326 - $listCoursesTemplate = ListCoursesTemplate::instance();
327 - $pagination_type = LP_Settings::get_option( 'course_pagination_type' );
328 -
329 - try {
330 - throw new Exception( 'Deprecated function from 4.3.7' );
331 - $filter = new CourseJsonFilter();
332 - $params = $request->get_params();
333 - $params['c_status'] = 'publish';
334 - $params['c_fields'] = 'ID, post_title, post_content, post_status, post_author, meta_input';
335 - CourseService::handle_params_for_query_list_courses( $filter, $params );
336 -
337 - // Check is in category page.
338 - /*if ( ! empty( $request->get_param( 'page_term_id_current' ) ) &&
339 - empty( $request->get_param( 'term_id' ) ) ) {
340 - $filter->term_ids[] = $request->get_param( 'page_term_id_current' );
341 - } // Check is in tag page.
342 - elseif ( ! empty( $request->get_param( 'page_tag_id_current' ) ) &&
343 - empty( $request->get_param( 'tag_id' ) ) ) {
344 - $filter->tag_ids[] = $request->get_param( 'page_tag_id_current' );
345 - }*/
346 -
347 - $total_rows = 0;
348 - $courses = CourseService::get_list_courses( $filter, $total_rows );
349 - $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
350 - $return_type = $request['return_type'] ?? 'html';
351 - if ( 'json' === $return_type ) {
352 - $response->data->courses = $courses;
353 - $response->data->total_pages = $total_pages;
354 - } else {
355 - // For return data has html
356 - ob_start();
357 - if ( $courses ) {
358 - if ( ! empty( $request['c_suggest'] ) ) {
359 - $data = array(
360 - 'courses' => $courses,
361 - 'keyword' => $request['c_search'],
362 - 'total_course' => $total_rows,
363 - );
364 - do_action( 'learn-press/rest-api/courses/suggest/layout', $data );
365 - } else {
366 - global $wp, $post;
367 -
368 - // Template Pagination.
369 - switch ( $pagination_type ) {
370 - case 'load-more':
371 - if ( $filter->page < $total_pages ) {
372 - $response->data->pagination = $listCoursesTemplate->html_pagination_load_more();
373 - }
374 - break;
375 - case 'infinite':
376 - if ( $filter->page < $total_pages ) {
377 - $response->data->pagination = $listCoursesTemplate->html_pagination_infinite();
378 - }
379 - break;
380 - default:
381 - $pagination_args = [
382 - 'total_pages' => $total_pages,
383 - 'paged' => $filter->page,
384 - 'base' => add_query_arg( 'paged', '%#%', learn_press_get_page_link( 'courses' ) ),
385 - ];
386 - $response->data->pagination = $listCoursesTemplate->html_pagination_number( $pagination_args );
387 - break;
388 - }
389 - $response->data->pagination_type = $pagination_type;
390 - // End Pagination
391 -
392 - // For custom template
393 - $template_path = apply_filters( 'lp/api/courses/template', '', $request );
394 - if ( ! empty( $template_path ) ) {
395 - Template::instance()->get_template( $template_path, compact( 'courses', 'total_pages', 'request' ) );
396 - } else {
397 - foreach ( $courses as $course ) {
398 - $post = get_post( $course->ID );
399 - setup_postdata( $post );
400 - Template::instance()->get_frontend_template( 'content-course.php' );
401 - }
402 -
403 - wp_reset_postdata();
404 - }
405 - // End content items
406 - }
407 - } else {
408 - LearnPress::instance()->template( 'course' )->no_courses_found();
409 - }
410 -
411 - $response->data->content = ob_get_clean();
412 - $response->data->totals = $total_rows;
413 -
414 - $from = 1 + ( $filter->page - 1 ) * $filter->limit;
415 - $to = ( $filter->page * $filter->limit > $total_rows ) ? $total_rows : $filter->page * $filter->limit;
416 -
417 - if ( 0 === $total_rows ) {
418 - $response->data->from_to = '';
419 - } elseif ( 1 === $total_rows ) {
420 - $response->data->from_to = esc_html__( 'Showing only one result', 'learnpress' );
421 - } else {
422 - if ( $from == $to ) {
423 - $response->data->from_to = sprintf( esc_html__( 'Showing last course of %s results', 'learnpress' ), $total_rows );
424 - } else {
425 - switch ( $pagination_type ) {
426 - case 'load-more':
427 - case 'infinite':
428 - $from_to = $filter->page * $filter->limit;
429 - if ( $from_to > $total_rows ) {
430 - $from_to = $total_rows;
431 - }
432 - break;
433 - default:
434 - $from_to = $from . '-' . $to;
435 - break;
436 - }
437 -
438 - $response->data->from_to = sprintf( esc_html__( 'Showing %1$s of %2$s results', 'learnpress' ), $from_to, $total_rows );
439 - }
440 - }
441 - }
442 -
443 - $response->status = 'success';
444 - } catch ( Throwable $e ) {
445 - $response->data->content = $e->getMessage();
446 - $response->message = $e->getMessage();
447 - }
448 -
449 - return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
450 - }
451 -
452 - /**
453 - * Get list courses - Widget Elementor
454 - *
455 - * @param WP_REST_Request $request
456 - *
457 - * @return LP_REST_Response
458 - */
459 - public function courses_widget_by_page( WP_REST_Request $request ): LP_REST_Response {
460 - $response = new LP_REST_Response();
461 - $response->data = new stdClass();
462 -
463 - try {
464 - $settings = array_merge(
465 - $request->get_params(),
466 - [
467 - 'courses_ul_classes' => [ 'list-courses-elm' ],
468 - ]
469 - );
470 - $response->data->content = ListCoursesByPageElementor::render_data_from_setting( $settings );
471 -
472 - $response->status = 'success';
473 - } catch ( Throwable $e ) {
474 - ob_end_clean();
475 - $response->data->content = $e->getMessage();
476 - $response->message = $e->getMessage();
477 - }
478 -
479 - return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
480 - }
481 -
482 - /**
483 - * Rest API for Enroll in single course.
484 - *
485 - * @param WP_REST_Request $request
486 - *
487 - * @return LP_REST_Response
488 - * @author Nhamdv
489 - * @editor tungnx
490 - * @version 1.0.3
491 - * @since 4.0.0
492 - */
493 - public function enroll_courses( WP_REST_Request $request ): LP_REST_Response {
494 - $response = new LP_REST_Response();
495 - $response->data = new stdClass();
496 -
497 - try {
498 - $course_id = absint( $request['id'] ?? 0 );
499 - $course = CourseModel::find( $course_id, true );
500 - if ( ! $course ) {
501 - throw new Exception( esc_html__( 'Invalid course!', 'learnpress' ) );
502 - }
503 -
504 - $user_id = get_current_user_id();
505 - $user = UserModel::find( $user_id, true );
506 - $can_enroll = $course->can_enroll( $user );
507 - if ( $can_enroll instanceof WP_Error ) {
508 - throw new Exception( $can_enroll->get_error_message() );
509 - }
510 -
511 - // Case: if user bought course
512 - $userCourse = UserCourseModel::find( $user_id, $course_id, true );
513 - if ( $userCourse && $userCourse->has_purchased() ) {
514 - $userCourse->graduation = LP_COURSE_GRADUATION_IN_PROGRESS;
515 - $userCourse->status = LP_COURSE_ENROLLED;
516 - $userCourse->start_time = gmdate( LP_Datetime::$format, time() );
517 - $userCourse->save();
518 -
519 - do_action( 'learnpress/user/course-enrolled', $userCourse->ref_id, $course_id, $user->get_id() );
520 -
521 - /**
522 - * Send mail user enrolled course
523 - * @uses SendEmailAjax::send_mail_user_enrolled_course()
524 - */
525 - $data_send = [
526 - $userCourse->ref_id,
527 - $course_id,
528 - $user->get_id(),
529 - ];
530 - LPBackgroundAjax::handle(
531 - [
532 - 'params' => $data_send,
533 - 'lp-load-ajax' => 'send_mail_user_enrolled_course',
534 - ]
535 - );
536 - } else { // Case enroll course free
537 - $cart = LearnPress::instance()->cart;
538 - $checkout = LP_Checkout::instance();
539 -
540 - if ( ! learn_press_enable_cart() ) {
541 - $cart->empty_cart();
542 - }
543 -
544 - $cart_id = $cart->add_to_cart( $course_id );
545 - if ( ! $cart_id ) {
546 - throw new Exception( esc_html__( 'Error: The course cannot be added to the cart.', 'learnpress' ) );
547 - }
548 -
549 - if ( is_user_logged_in() ) {
550 - $order_id = $checkout->create_order();
551 - $order = new LP_Order( $order_id );
552 - $order->payment_complete();
553 -
554 - $cart->empty_cart();
555 - }
556 - }
557 -
558 - if ( is_user_logged_in() ) {
559 - $response->message = esc_html__(
560 - 'Congrats! You have enrolled in the course successfully. Redirecting...',
561 - 'learnpress'
562 - );
563 -
564 - $first_item_id = $course->get_first_item_id();
565 - $response->data->redirect = $first_item_id ? $course->get_item_link( $first_item_id ) : get_the_permalink( $course->get_id() );
566 - } else {
567 - $redirect_url = LP_Page_Controller::get_link_page( 'checkout', [], true );
568 - $redirect_url = apply_filters(
569 - 'learnpress/rest-api/courses/enroll/redirect',
570 - $redirect_url,
571 - $course_id
572 - );
573 -
574 - if ( empty( $redirect_url ) ) {
575 - throw new Exception( __( 'Error: Please set up a page for checkout.', 'learnpress' ) );
576 - }
577 -
578 - $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
579 - $response->message = esc_html__( 'Redirecting...', 'learnpress' );
580 - $response->data->redirect = esc_url_raw( $redirect_url );
581 - }
582 -
583 - $response->status = 'success';
584 - } catch ( Exception $e ) {
585 - $response->message = $e->getMessage();
586 - }
587 -
588 - return $response;
589 - }
590 -
591 - /**
592 - * Rest API for Purchase course in single course.
593 - *
594 - * @param WP_REST_Request $request .
595 - *
596 - * @return WP_REST_Response|WP_Error
597 - * @throws Exception .
598 - * @author Nhamdv
599 - */
600 - public function purchase_course( WP_REST_Request $request ) {
601 - $response = new LP_REST_Response();
602 - $response->data = new stdClass();
603 - $params = $request->get_params();
604 -
605 - try {
606 - $course_id = $params['id'];
607 - $allow_repurchase_type = $params['repurchaseType'] ?? false;
608 -
609 - if ( ! $course_id ) {
610 - throw new Exception( __( 'Error: Invalid Course ID.', 'learnpress' ) );
611 - }
612 -
613 - $course = CourseModel::find( $course_id, true );
614 - if ( ! $course ) {
615 - throw new Exception( __( 'Error: No Course available.', 'learnpress' ) );
616 - }
617 -
618 - $user_id = get_current_user_id();
619 - $user = UserModel::find( $user_id, true );
620 - $can_purchase = $course->can_purchase( $user );
621 - if ( $can_purchase instanceof WP_Error ) {
622 - throw new Exception( $can_purchase->get_error_message() );
623 - }
624 -
625 - $userCourse = UserCourseModel::find( $user_id, $course_id, true );
626 - $option_repurchase = [
627 - 'reset' => esc_html__( 'Reset course progress', 'learnpress' ),
628 - 'keep' => esc_html__( 'Keep course progress', 'learnpress' ),
629 - ];
630 - $html_li_repurchase = '';
631 - foreach ( $option_repurchase as $key => $value ) {
632 - $html_li_repurchase .= sprintf(
633 - '<li><label>%s</label></li>',
634 - sprintf(
635 - '<input name="_lp_allow_repurchase_type" value="%s" type="radio" checked="checked"/> %s',
636 - $key,
637 - $value
638 - )
639 - );
640 - }
641 -
642 - $repurchase_type = $course->get_type_repurchase();
643 - $section_popup_repurchase = [
644 - 'wrapper' => '<div class="lp_allow_repurchase_select">',
645 - 'ul' => '<ul>',
646 - 'list' => $html_li_repurchase,
647 - 'ul_end' => '</ul>',
648 - 'wrapper_end' => '</div>',
649 - ];
650 - if ( $course->enable_allow_repurchase()
651 - && $user_id && $userCourse
652 - && empty( $allow_repurchase_type ) ) {
653 - if ( $repurchase_type === 'popup' ) {
654 - $response->data->html = Template::combine_components( $section_popup_repurchase );
655 - $response->data->type = 'allow_repurchase';
656 - $response->data->titlePopup = esc_html__( 'Repurchase Options', 'learnpress' );
657 - $response->status = 'success';
658 -
659 - return rest_ensure_response( $response );
660 - }
661 - }
662 -
663 - $cart = LearnPress::instance()->cart;
664 - if ( ! learn_press_enable_cart() ) {
665 - $cart->empty_cart();
666 - }
667 -
668 - // @deprecated hook since v4.2.7.3
669 - //do_action( 'learnpress/rest-api/courses/purchase/before-add-to-cart' );
670 -
671 - $cart_id = $cart->add_to_cart( $course_id, 1, $params );
672 - if ( empty( $cart_id ) ) {
673 - throw new Exception( __( 'Error: The course cannot be added to the cart.', 'learnpress' ) );
674 - }
675 -
676 - if ( ! empty( $allow_repurchase_type ) && $userCourse ) {
677 - learn_press_update_user_item_meta( $userCourse->get_user_item_id(), '_lp_allow_repurchase_type', $allow_repurchase_type );
678 - }
679 -
680 - $redirect_url = LP_Page_Controller::get_link_page( 'checkout', [], true );
681 - $redirect_url = apply_filters(
682 - 'learnpress/rest-api/courses/purchase/redirect',
683 - $redirect_url,
684 - $course_id,
685 - $cart_id
686 - );
687 -
688 - if ( empty( $redirect_url ) ) {
689 - throw new Exception( __( 'Error: Please set up a page for checkout.', 'learnpress' ) );
690 - }
691 -
692 - $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
693 - $response->status = 'success';
694 - $response->message = sprintf(
695 - esc_html__( '"%s" has been added to your cart. Redirecting...', 'learnpress' ),
696 - $course->get_title()
697 - );
698 - $response->data->redirect = esc_url_raw( $redirect_url );
699 - } catch ( Exception $e ) {
700 - $response->message = $e->getMessage();
701 - }
702 -
703 - return rest_ensure_response( $response );
704 - }
705 -
706 - /**
707 - * Rest API for retake course.
708 - *
709 - * @param WP_REST_Request $request .
710 - *
711 - * @throws Exception .
712 - */
713 - public function retake_course( WP_REST_Request $request ) {
714 - $response = new LP_REST_Response();
715 -
716 - try {
717 - $course_id = $request->get_param( 'id' );
718 -
719 - if ( ! $course_id ) {
720 - throw new Exception( __( 'Invalid params', 'learnpress' ) );
721 - }
722 -
723 - $courseModel = CourseModel::find( $course_id, true );
724 - if ( ! $courseModel ) {
725 - throw new Exception( __( 'Invalid course', 'learnpress' ) );
726 - }
727 -
728 - $userModel = UserModel::find( get_current_user_id(), true );
729 - if ( ! $userModel ) {
730 - throw new Exception( __( 'Invalid user', 'learnpress' ) );
731 - }
732 -
733 - $userCourseModel = UserCourseModel::find( $userModel->get_id(), $courseModel->get_id(), true );
734 - if ( ! $userCourseModel ) {
735 - throw new Exception( __( 'Invalid user course', 'learnpress' ) );
736 - }
737 -
738 - $userCourseModel->handle_retake();
739 - $item_continue = $userCourseModel->get_item_continue();
740 - if ( $item_continue ) {
741 - $link_continue = $courseModel->get_item_link( $item_continue->ID );
742 - } else {
743 - $link_continue = $courseModel->get_permalink();
744 - }
745 -
746 - $response->status = 'success';
747 - $response->message = esc_html__( 'Now you can begin this course', 'learnpress' );
748 - $response->data->url_redirect = $link_continue;
749 - } catch ( Exception $e ) {
750 - $response->message = $e->getMessage();
751 - }
752 -
753 - return $response;
754 - }
755 -
756 - /**
757 - * @param WP_REST_Request $request
758 - *
759 - * @return WP_REST_Response
760 - */
761 - public function get_items( $request ) {
762 - $settings = LP_Settings::instance();
763 - $response = array(
764 - 'result' => $settings->get(),
765 - );
766 -
767 - return rest_ensure_response( $response );
768 - }
769 -
770 - /**
771 - * @param WP_REST_Request $request
772 - *
773 - * @return WP_REST_Response
774 - */
775 - public function get_item( $request ) {
776 - $settings = LP_Settings::instance();
777 - $response = array(
778 - 'result' => $settings->get( $request['key'] ),
779 - );
780 -
781 - return rest_ensure_response( $response );
782 - }
783 -
784 - /**
785 - * @param WP_REST_Request $request
786 - *
787 - * @return WP_REST_Response
788 - */
789 - public function update_item( $request ) {
790 - $response = array();
791 - $settings = LP_Settings::instance();
792 - $option = $settings->get( $request['key'] );
793 -
794 - $settings->update( $request['key'], $request['data'] );
795 - $new_option = $settings->get( $request['key'] );
796 - $success = maybe_serialize( $option ) !== maybe_serialize( $new_option );
797 -
798 - $response['success'] = $success;
799 - $response['result'] = $success ? $new_option : $option;
800 -
801 - return rest_ensure_response( $response );
802 - }
803 -
804 - /**
805 - * @param WP_REST_Request $request
806 - *
807 - * @return WP_REST_Response
808 - */
809 - public function delete_item( $request ) {
810 - $response = array();
811 -
812 - return rest_ensure_response( $response );
813 - }
814 -
815 - /**
816 - * Rest API for get item continue in single course.
817 - *
818 - * @param WP_REST_Request $request
819 - *
820 - * @return LP_REST_Response
821 - * @since 4.1.4
822 - * @version 1.0.4
823 - */
824 - public function continue_course( WP_REST_Request $request ): LP_REST_Response {
825 - $params = $request->get_params();
826 - $response = new LP_REST_Response();
827 - $response->data = '';
828 -
829 - try {
830 - $flag_found = false;
831 - $item_link = '';
832 - $course_id = absint( $params['courseId'] ?? 0 );
833 - $user_id = absint( $params['userId'] ?? 0 );
834 -
835 - $user = UserModel::find( $user_id, true );
836 - $course = CourseModel::find( $course_id, true );
837 - if ( ! $course ) {
838 - throw new Exception( __( 'Invalid course', 'learnpress' ) );
839 - }
840 -
841 - if ( ! $user ) {
842 - return $response;
843 - }
844 -
845 - $userCourseModel = UserCourseModel::find( $user_id, $course_id, true );
846 - if ( ! $userCourseModel ) {
847 - throw new Exception( __( 'Invalid user course', 'learnpress' ) );
848 - }
849 -
850 - $sections_items = $course->get_section_items();
851 - $total_items = $course->get_total_items();
852 -
853 - if ( ! empty( $total_items ) ) {
854 - foreach ( $sections_items as $section_items ) {
855 - if ( $flag_found ) {
856 - break;
857 - }
858 -
859 - foreach ( $section_items->items as $item ) {
860 - $item_id = $item->id ?? $item->item_id;
861 - $item_type = $item->type ?? $item->item_type;
862 -
863 - $userItemModel = UserItemModel::find_user_item(
864 - $user_id,
865 - $item_id,
866 - $item_type,
867 - $course_id,
868 - LP_COURSE_CPT,
869 - true
870 - );
871 - if ( ! $userItemModel || $userItemModel->get_status() !== LP_ITEM_COMPLETED ) {
872 - $item_link = $course->get_item_link( $item->id );
873 - $flag_found = true;
874 - break;
875 - }
876 - }
877 - }
878 -
879 - if ( ! $flag_found ) {
880 - $item_link = $course->get_item_link( $course->get_first_item_id() );
881 - }
882 - }
883 -
884 - $response->data = $item_link;
885 - $response->status = 'success';
886 - $response->message = '';
887 - } catch ( Exception $e ) {
888 - $response->message = $e->getMessage();
889 - }
890 -
891 - return $response;
892 - }
893 -}
1 +<?php
2 +
3 +/**
4 + * Class LP_REST_Courses_Controller
5 + */
6 +
7 +use LearnPress\Background\LPBackgroundAjax;
8 +use LearnPress\ExternalPlugin\Elementor\Widgets\Course\ListCoursesByPageElementor;
9 +use LearnPress\Filters\Course\CourseJsonFilter;
10 +use LearnPress\Helpers\Response;
11 +use LearnPress\Helpers\Template;
12 +use LearnPress\Models\CourseModel;
13 +use LearnPress\Models\PostModel;
14 +use LearnPress\Models\UserItems\UserCourseModel;
15 +use LearnPress\Models\UserItems\UserItemModel;
16 +use LearnPress\Models\UserModel;
17 +use LearnPress\Services\CourseService;
18 +use LearnPress\TemplateHooks\Course\ListCoursesTemplate;
19 +use LearnPress\TemplateHooks\Course\SingleCourseTemplate;
20 +
21 +class LP_REST_Courses_Controller extends LP_Abstract_REST_Controller {
22 + /**
23 + * LP_REST_Courses_Controller constructor.
24 + */
25 + public function __construct() {
26 + $this->namespace = 'lp/v1';
27 + $this->rest_base = 'courses';
28 + parent::__construct();
29 + }
30 +
31 + /**
32 + * Register routes API
33 + */
34 + public function register_routes() {
35 + $this->routes = array(
36 + '' => array(
37 + array(
38 + 'methods' => WP_REST_Server::ALLMETHODS,
39 + 'callback' => array( $this, 'get_courses' ),
40 + ),
41 + ),
42 + 'edit-archive-block' => array(
43 + array(
44 + 'methods' => WP_REST_Server::ALLMETHODS,
45 + 'callback' => array( $this, 'get_courses_archive_block' ),
46 + 'permission_callback' => '__return_true',
47 + 'args' => array(),
48 + ),
49 + ),
50 + 'courses-suggest' => array(
51 + array(
52 + 'methods' => WP_REST_Server::ALLMETHODS,
53 + 'callback' => array( $this, 'courses_suggest' ),
54 + 'permission_callback' => '__return_true',
55 + 'args' => [],
56 + ),
57 + ),
58 + 'purchase-course' => array(
59 + array(
60 + 'methods' => WP_REST_Server::CREATABLE,
61 + 'callback' => array( $this, 'purchase_course' ),
62 + 'permission_callback' => '__return_true',
63 + ),
64 + ),
65 + 'enroll-course' => array(
66 + array(
67 + 'methods' => WP_REST_Server::CREATABLE,
68 + 'callback' => array( $this, 'enroll_courses' ),
69 + 'permission_callback' => '__return_true',
70 + ),
71 + ),
72 + 'retake-course' => array(
73 + array(
74 + 'methods' => WP_REST_Server::CREATABLE,
75 + 'callback' => array( $this, 'retake_course' ),
76 + 'permission_callback' => function () {
77 + return is_user_logged_in();
78 + },
79 + 'schema' => array(
80 + 'type' => 'int',
81 + ),
82 + ),
83 + ),
84 + /*'archive-course' => array(
85 + array(
86 + 'methods' => WP_REST_Server::ALLMETHODS,
87 + 'callback' => array( $this, 'list_courses' ),
88 + 'permission_callback' => '__return_true',
89 + 'args' => [],
90 + ),
91 + ),*/
92 + 'courses-widget-by-page' => array(
93 + array(
94 + 'methods' => WP_REST_Server::ALLMETHODS,
95 + 'callback' => array( $this, 'courses_widget_by_page' ),
96 + 'permission_callback' => '__return_true',
97 + 'args' => [],
98 + ),
99 + ),
100 + '(?P<key>[\w]+)' => array(
101 + 'args' => array(
102 + 'id' => array(
103 + 'description' => __( 'A unique identifier for the resource.', 'learnpress' ),
104 + 'type' => 'string',
105 + ),
106 + ),
107 + array(
108 + 'methods' => WP_REST_Server::READABLE,
109 + 'callback' => array( $this, 'get_item' ),
110 + 'permission_callback' => array( $this, 'check_admin_permission' ),
111 + ),
112 + array(
113 + 'methods' => WP_REST_Server::EDITABLE,
114 + 'callback' => array( $this, 'update_item' ),
115 + 'permission_callback' => array( $this, 'check_admin_permission' ),
116 + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
117 + ),
118 + array(
119 + 'methods' => WP_REST_Server::DELETABLE,
120 + 'callback' => array( $this, 'delete_item' ),
121 + 'permission_callback' => array( $this, 'check_admin_permission' ),
122 + ),
123 + 'schema' => array( $this, 'get_public_item_schema' ),
124 + ),
125 + 'continue-course' => array(
126 + array(
127 + 'methods' => WP_REST_Server::CREATABLE,
128 + 'callback' => array( $this, 'continue_course' ),
129 + 'permission_callback' => '__return_true',
130 + ),
131 + ),
132 + );
133 +
134 + parent::register_routes();
135 + }
136 +
137 + /**
138 + * Check user is Admin
139 + *
140 + * @return bool
141 + */
142 + public function check_admin_permission(): bool {
143 + return LP_Abstract_API::check_admin_permission();
144 + }
145 +
146 + /**
147 + * Get list courses, return JSON data, not to handle HTML
148 + *
149 + * @param WP_REST_Request $request
150 + *
151 + * @return Response
152 + * @since 4.2.8.2
153 + * @version 1.0.1
154 + */
155 + public function get_courses( WP_REST_Request $request ): Response {
156 + $response = new Response();
157 +
158 + try {
159 + $filter = new CourseJsonFilter();
160 + $params = $request->get_params();
161 + $total_rows = 0;
162 +
163 + CourseService::handle_params_for_query_list_courses( $filter, $params );
164 + $filter->only_fields = [ CourseJsonFilter::COL_ID ];
165 +
166 + $coursesRs = CourseService::get_list_courses( $filter, $total_rows );
167 +
168 + $courses = [];
169 + foreach ( $coursesRs as $course ) {
170 + $courseModel = CourseModel::find( $course->ID, true );
171 + $allow_fields = apply_filters(
172 + 'lp/rest-api/courses/allow_fields',
173 + [
174 + CourseJsonFilter::COL_ID,
175 + CourseJsonFilter::COL_POST_TITLE,
176 + CourseJsonFilter::COL_POST_NAME,
177 + CourseJsonFilter::COL_POST_AUTHOR,
178 + CourseJsonFilter::COL_LANG,
179 + CourseJsonFilter::COL_IS_SALE,
180 + ],
181 + $courseModel,
182 + $params
183 + );
184 + if ( ! $courseModel ) {
185 + continue;
186 + }
187 +
188 + $course_info = new stdClass();
189 + foreach ( $allow_fields as $field ) {
190 + $course_info->$field = $courseModel->$field;
191 + }
192 +
193 + $courses[] = $course_info;
194 + }
195 +
196 + $response->status = Response::STATUS_SUCCESS;
197 + $response->data->courses = $courses;
198 + $response->data->total = $total_rows;
199 + $response->data->page = $filter->page;
200 + $response->data->total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
201 + } catch ( Throwable $e ) {
202 + $response->message = $e->getMessage();
203 + }
204 +
205 + return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
206 + }
207 +
208 + /**
209 + * Get list courses display layout when edit on archive block
210 + *
211 + * @param WP_REST_Request $request
212 + *
213 + * @return Response
214 + * @since 4.3.7
215 + * @version 1.0.0
216 + */
217 + public function get_courses_archive_block( WP_REST_Request $request ): Response {
218 + $response = new Response();
219 +
220 + try {
221 + $filter = new CourseJsonFilter();
222 + $params = $request->get_params();
223 + $total_rows = 0;
224 +
225 + CourseService::handle_params_for_query_list_courses( $filter, $params );
226 + // For permission, it only displays for Editor easy config layout, not for get data primary
227 + $filter->only_fields = [ CourseJsonFilter::COL_ID ];
228 + $filter->post_status = [ PostModel::STATUS_PUBLISH ];
229 + $coursesRs = CourseService::get_list_courses( $filter, $total_rows );
230 +
231 + $courses = [];
232 + $singleCourseTemplate = SingleCourseTemplate::instance();
233 + foreach ( $coursesRs as $course ) {
234 + $courseModel = CourseModel::find( $course->ID, true );
235 + if ( ! $courseModel ) {
236 + continue;
237 + }
238 +
239 + $courseItem = new stdClass();
240 + $courseItem->ID = $course->ID;
241 + $courseItem->description = $singleCourseTemplate->html_short_description( $courseModel );
242 + $courseItem->price = $singleCourseTemplate->html_price( $courseModel );
243 + $courseItem->title = $courseModel->get_title();
244 + $courseItem->student = $singleCourseTemplate->html_count_student( $courseModel );
245 + $courseItem->lesson = $singleCourseTemplate->html_count_item( $courseModel, LP_LESSON_CPT );
246 + $courseItem->duration = $singleCourseTemplate->html_duration( $courseModel );
247 + $courseItem->quiz = $singleCourseTemplate->html_count_item( $courseModel, LP_QUIZ_CPT );
248 + $courseItem->level = $singleCourseTemplate->html_level( $courseModel );
249 + $courseItem->image = $singleCourseTemplate->html_image( $courseModel );
250 + $courseItem->instructor = $singleCourseTemplate->html_instructor( $courseModel, false, [ 'is_link' => false ] );
251 + $courseItem->category = $singleCourseTemplate->html_categories( $courseModel );
252 + $courseItem->button = __( 'Read more', 'learnpress' );
253 +
254 + $courses[] = apply_filters( 'lp/rest-api/frontend/course/archive_course/courses', $courseItem, $courseModel );
255 + }
256 +
257 + $response->status = Response::STATUS_SUCCESS;
258 + $response->data->courses = $courses;
259 + $response->data->total = $total_rows;
260 + $response->data->page = $filter->page;
261 + $response->data->total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
262 + } catch ( Throwable $e ) {
263 + $response->message = $e->getMessage();
264 + }
265 +
266 + return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
267 + }
268 +
269 + /**
270 + * Get list courses for search suggest
271 + *
272 + * @param WP_REST_Request $request
273 + *
274 + * @return Response
275 + * @since 4.3.7
276 + * @version 1.0.0
277 + */
278 + public function courses_suggest( WP_REST_Request $request ): Response {
279 + $response = new Response();
280 +
281 + try {
282 + $filter = new CourseJsonFilter();
283 + $params = $request->get_params();
284 + $total_rows = 0;
285 +
286 + CourseService::handle_params_for_query_list_courses( $filter, $params );
287 + $filter->only_fields = [ CourseJsonFilter::COL_ID ];
288 + $filter->post_status = [ PostModel::STATUS_PUBLISH ];
289 +
290 + $coursesRs = CourseService::get_list_courses( $filter, $total_rows );
291 +
292 + $courses = [];
293 + foreach ( $coursesRs as $course ) {
294 + $courseModel = CourseModel::find( $course->ID, true );
295 + $courses[] = $courseModel;
296 + }
297 +
298 + $data = array(
299 + 'courses' => $courses,
300 + 'keyword' => $request['c_search'],
301 + 'total_course' => $total_rows,
302 + );
303 + ob_start();
304 + do_action( 'learn-press/rest-api/courses/suggest/layout', $data );
305 + $response->data->content = ob_get_clean();
306 + $response->data->totals = $total_rows;
307 + $response->status = Response::STATUS_SUCCESS;
308 + } catch ( Throwable $e ) {
309 + $response->message = $e->getMessage();
310 + }
311 +
312 + return apply_filters( 'lp/rest-api/courses-search-suggest/response', $response );
313 + }
314 +
315 + /**
316 + * Get list courses
317 + *
318 + * @param WP_REST_Request $request
319 + *
320 + * @depreacted 4.3.7
321 + * @return LP_REST_Response
322 + */
323 + public function list_courses( WP_REST_Request $request ): LP_REST_Response {
324 + $response = new LP_REST_Response();
325 + $response->data = new stdClass();
326 + $listCoursesTemplate = ListCoursesTemplate::instance();
327 + $pagination_type = LP_Settings::get_option( 'course_pagination_type' );
328 +
329 + try {
330 + throw new Exception( 'Deprecated function from 4.3.7' );
331 + $filter = new CourseJsonFilter();
332 + $params = $request->get_params();
333 + $params['c_status'] = 'publish';
334 + $params['c_fields'] = 'ID, post_title, post_content, post_status, post_author, meta_input';
335 + CourseService::handle_params_for_query_list_courses( $filter, $params );
336 +
337 + // Check is in category page.
338 + /*if ( ! empty( $request->get_param( 'page_term_id_current' ) ) &&
339 + empty( $request->get_param( 'term_id' ) ) ) {
340 + $filter->term_ids[] = $request->get_param( 'page_term_id_current' );
341 + } // Check is in tag page.
342 + elseif ( ! empty( $request->get_param( 'page_tag_id_current' ) ) &&
343 + empty( $request->get_param( 'tag_id' ) ) ) {
344 + $filter->tag_ids[] = $request->get_param( 'page_tag_id_current' );
345 + }*/
346 +
347 + $total_rows = 0;
348 + $courses = CourseService::get_list_courses( $filter, $total_rows );
349 + $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
350 + $return_type = $request['return_type'] ?? 'html';
351 + if ( 'json' === $return_type ) {
352 + $response->data->courses = $courses;
353 + $response->data->total_pages = $total_pages;
354 + } else {
355 + // For return data has html
356 + ob_start();
357 + if ( $courses ) {
358 + if ( ! empty( $request['c_suggest'] ) ) {
359 + $data = array(
360 + 'courses' => $courses,
361 + 'keyword' => $request['c_search'],
362 + 'total_course' => $total_rows,
363 + );
364 + do_action( 'learn-press/rest-api/courses/suggest/layout', $data );
365 + } else {
366 + global $wp, $post;
367 +
368 + // Template Pagination.
369 + switch ( $pagination_type ) {
370 + case 'load-more':
371 + if ( $filter->page < $total_pages ) {
372 + $response->data->pagination = $listCoursesTemplate->html_pagination_load_more();
373 + }
374 + break;
375 + case 'infinite':
376 + if ( $filter->page < $total_pages ) {
377 + $response->data->pagination = $listCoursesTemplate->html_pagination_infinite();
378 + }
379 + break;
380 + default:
381 + $pagination_args = [
382 + 'total_pages' => $total_pages,
383 + 'paged' => $filter->page,
384 + 'base' => add_query_arg( 'paged', '%#%', learn_press_get_page_link( 'courses' ) ),
385 + ];
386 + $response->data->pagination = $listCoursesTemplate->html_pagination_number( $pagination_args );
387 + break;
388 + }
389 + $response->data->pagination_type = $pagination_type;
390 + // End Pagination
391 +
392 + // For custom template
393 + $template_path = apply_filters( 'lp/api/courses/template', '', $request );
394 + if ( ! empty( $template_path ) ) {
395 + Template::instance()->get_template( $template_path, compact( 'courses', 'total_pages', 'request' ) );
396 + } else {
397 + foreach ( $courses as $course ) {
398 + $post = get_post( $course->ID );
399 + setup_postdata( $post );
400 + Template::instance()->get_frontend_template( 'content-course.php' );
401 + }
402 +
403 + wp_reset_postdata();
404 + }
405 + // End content items
406 + }
407 + } else {
408 + LearnPress::instance()->template( 'course' )->no_courses_found();
409 + }
410 +
411 + $response->data->content = ob_get_clean();
412 + $response->data->totals = $total_rows;
413 +
414 + $from = 1 + ( $filter->page - 1 ) * $filter->limit;
415 + $to = ( $filter->page * $filter->limit > $total_rows ) ? $total_rows : $filter->page * $filter->limit;
416 +
417 + if ( 0 === $total_rows ) {
418 + $response->data->from_to = '';
419 + } elseif ( 1 === $total_rows ) {
420 + $response->data->from_to = esc_html__( 'Showing only one result', 'learnpress' );
421 + } else {
422 + if ( $from == $to ) {
423 + $response->data->from_to = sprintf( esc_html__( 'Showing last course of %s results', 'learnpress' ), $total_rows );
424 + } else {
425 + switch ( $pagination_type ) {
426 + case 'load-more':
427 + case 'infinite':
428 + $from_to = $filter->page * $filter->limit;
429 + if ( $from_to > $total_rows ) {
430 + $from_to = $total_rows;
431 + }
432 + break;
433 + default:
434 + $from_to = $from . '-' . $to;
435 + break;
436 + }
437 +
438 + $response->data->from_to = sprintf( esc_html__( 'Showing %1$s of %2$s results', 'learnpress' ), $from_to, $total_rows );
439 + }
440 + }
441 + }
442 +
443 + $response->status = 'success';
444 + } catch ( Throwable $e ) {
445 + $response->data->content = $e->getMessage();
446 + $response->message = $e->getMessage();
447 + }
448 +
449 + return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
450 + }
451 +
452 + /**
453 + * Get list courses - Widget Elementor
454 + *
455 + * @param WP_REST_Request $request
456 + *
457 + * @return LP_REST_Response
458 + */
459 + public function courses_widget_by_page( WP_REST_Request $request ): LP_REST_Response {
460 + $response = new LP_REST_Response();
461 + $response->data = new stdClass();
462 +
463 + try {
464 + $settings = array_merge(
465 + $request->get_params(),
466 + [
467 + 'courses_ul_classes' => [ 'list-courses-elm' ],
468 + ]
469 + );
470 + $response->data->content = ListCoursesByPageElementor::render_data_from_setting( $settings );
471 +
472 + $response->status = 'success';
473 + } catch ( Throwable $e ) {
474 + ob_end_clean();
475 + $response->data->content = $e->getMessage();
476 + $response->message = $e->getMessage();
477 + }
478 +
479 + return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
480 + }
481 +
482 + /**
483 + * Rest API for Enroll in single course.
484 + *
485 + * @param WP_REST_Request $request
486 + *
487 + * @return LP_REST_Response
488 + * @author Nhamdv
489 + * @editor tungnx
490 + * @version 1.0.3
491 + * @since 4.0.0
492 + */
493 + public function enroll_courses( WP_REST_Request $request ): LP_REST_Response {
494 + $response = new LP_REST_Response();
495 + $response->data = new stdClass();
496 +
497 + try {
498 + $course_id = absint( $request['id'] ?? 0 );
499 + $course = CourseModel::find( $course_id, true );
500 + if ( ! $course ) {
501 + throw new Exception( esc_html__( 'Invalid course!', 'learnpress' ) );
502 + }
503 +
504 + $user_id = get_current_user_id();
505 + $user = UserModel::find( $user_id, true );
506 + $can_enroll = $course->can_enroll( $user );
507 + if ( $can_enroll instanceof WP_Error ) {
508 + throw new Exception( $can_enroll->get_error_message() );
509 + }
510 +
511 + // Case: if user bought course
512 + $userCourse = UserCourseModel::find( $user_id, $course_id, true );
513 + if ( $userCourse && $userCourse->has_purchased() ) {
514 + $userCourse->graduation = LP_COURSE_GRADUATION_IN_PROGRESS;
515 + $userCourse->status = LP_COURSE_ENROLLED;
516 + $userCourse->start_time = gmdate( LP_Datetime::$format, time() );
517 + $userCourse->save();
518 +
519 + do_action( 'learnpress/user/course-enrolled', $userCourse->ref_id, $course_id, $user->get_id() );
520 +
521 + /**
522 + * Send mail user enrolled course
523 + * @uses SendEmailAjax::send_mail_user_enrolled_course()
524 + */
525 + $data_send = [
526 + $userCourse->ref_id,
527 + $course_id,
528 + $user->get_id(),
529 + ];
530 + LPBackgroundAjax::handle(
531 + [
532 + 'params' => $data_send,
533 + 'lp-load-ajax' => 'send_mail_user_enrolled_course',
534 + ]
535 + );
536 + } else { // Case enroll course free
537 + $cart = LearnPress::instance()->cart;
538 + $checkout = LP_Checkout::instance();
539 +
540 + if ( ! learn_press_enable_cart() ) {
541 + $cart->empty_cart();
542 + }
543 +
544 + $cart_id = $cart->add_to_cart( $course_id );
545 + if ( ! $cart_id ) {
546 + throw new Exception( esc_html__( 'Error: The course cannot be added to the cart.', 'learnpress' ) );
547 + }
548 +
549 + if ( is_user_logged_in() ) {
550 + $order_id = $checkout->create_order();
551 + $order = new LP_Order( $order_id );
552 + $order->payment_complete();
553 +
554 + $cart->empty_cart();
555 + }
556 + }
557 +
558 + if ( is_user_logged_in() ) {
559 + $response->message = esc_html__(
560 + 'Congrats! You have enrolled in the course successfully. Redirecting...',
561 + 'learnpress'
562 + );
563 +
564 + $first_item_id = $course->get_first_item_id();
565 + $response->data->redirect = $first_item_id ? $course->get_item_link( $first_item_id ) : get_the_permalink( $course->get_id() );
566 + } else {
567 + $redirect_url = LP_Page_Controller::get_link_page( 'checkout', [], true );
568 + $redirect_url = apply_filters(
569 + 'learnpress/rest-api/courses/enroll/redirect',
570 + $redirect_url,
571 + $course_id
572 + );
573 +
574 + if ( empty( $redirect_url ) ) {
575 + throw new Exception( __( 'Error: Please set up a page for checkout.', 'learnpress' ) );
576 + }
577 +
578 + $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
579 + $response->message = esc_html__( 'Redirecting...', 'learnpress' );
580 + $response->data->redirect = esc_url_raw( $redirect_url );
581 + }
582 +
583 + $response->status = 'success';
584 + } catch ( Exception $e ) {
585 + $response->message = $e->getMessage();
586 + }
587 +
588 + return $response;
589 + }
590 +
591 + /**
592 + * Rest API for Purchase course in single course.
593 + *
594 + * @param WP_REST_Request $request .
595 + *
596 + * @return WP_REST_Response|WP_Error
597 + * @throws Exception .
598 + * @author Nhamdv
599 + */
600 + public function purchase_course( WP_REST_Request $request ) {
601 + $response = new LP_REST_Response();
602 + $response->data = new stdClass();
603 + $params = $request->get_params();
604 +
605 + try {
606 + $course_id = $params['id'];
607 + $allow_repurchase_type = $params['repurchaseType'] ?? false;
608 +
609 + if ( ! $course_id ) {
610 + throw new Exception( __( 'Error: Invalid Course ID.', 'learnpress' ) );
611 + }
612 +
613 + $course = CourseModel::find( $course_id, true );
614 + if ( ! $course ) {
615 + throw new Exception( __( 'Error: No Course available.', 'learnpress' ) );
616 + }
617 +
618 + $user_id = get_current_user_id();
619 + $user = UserModel::find( $user_id, true );
620 + $can_purchase = $course->can_purchase( $user );
621 + if ( $can_purchase instanceof WP_Error ) {
622 + throw new Exception( $can_purchase->get_error_message() );
623 + }
624 +
625 + $userCourse = UserCourseModel::find( $user_id, $course_id, true );
626 + $option_repurchase = [
627 + 'reset' => esc_html__( 'Reset course progress', 'learnpress' ),
628 + 'keep' => esc_html__( 'Keep course progress', 'learnpress' ),
629 + ];
630 + $html_li_repurchase = '';
631 + foreach ( $option_repurchase as $key => $value ) {
632 + $html_li_repurchase .= sprintf(
633 + '<li><label>%s</label></li>',
634 + sprintf(
635 + '<input name="_lp_allow_repurchase_type" value="%s" type="radio" checked="checked"/> %s',
636 + $key,
637 + $value
638 + )
639 + );
640 + }
641 +
642 + $repurchase_type = $course->get_type_repurchase();
643 + $section_popup_repurchase = [
644 + 'wrapper' => '<div class="lp_allow_repurchase_select">',
645 + 'ul' => '<ul>',
646 + 'list' => $html_li_repurchase,
647 + 'ul_end' => '</ul>',
648 + 'wrapper_end' => '</div>',
649 + ];
650 + if ( $course->enable_allow_repurchase()
651 + && $user_id && $userCourse
652 + && empty( $allow_repurchase_type ) ) {
653 + if ( $repurchase_type === 'popup' ) {
654 + $response->data->html = Template::combine_components( $section_popup_repurchase );
655 + $response->data->type = 'allow_repurchase';
656 + $response->data->titlePopup = esc_html__( 'Repurchase Options', 'learnpress' );
657 + $response->status = 'success';
658 +
659 + return rest_ensure_response( $response );
660 + }
661 + }
662 +
663 + $cart = LearnPress::instance()->cart;
664 + if ( ! learn_press_enable_cart() ) {
665 + $cart->empty_cart();
666 + }
667 +
668 + // @deprecated hook since v4.2.7.3
669 + //do_action( 'learnpress/rest-api/courses/purchase/before-add-to-cart' );
670 +
671 + $cart_id = $cart->add_to_cart( $course_id, 1, $params );
672 + if ( empty( $cart_id ) ) {
673 + throw new Exception( __( 'Error: The course cannot be added to the cart.', 'learnpress' ) );
674 + }
675 +
676 + if ( ! empty( $allow_repurchase_type ) && $userCourse ) {
677 + learn_press_update_user_item_meta( $userCourse->get_user_item_id(), '_lp_allow_repurchase_type', $allow_repurchase_type );
678 + }
679 +
680 + $redirect_url = LP_Page_Controller::get_link_page( 'checkout', [], true );
681 + $redirect_url = apply_filters(
682 + 'learnpress/rest-api/courses/purchase/redirect',
683 + $redirect_url,
684 + $course_id,
685 + $cart_id
686 + );
687 +
688 + if ( empty( $redirect_url ) ) {
689 + throw new Exception( __( 'Error: Please set up a page for checkout.', 'learnpress' ) );
690 + }
691 +
692 + $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
693 + $response->status = 'success';
694 + $response->message = sprintf(
695 + esc_html__( '"%s" has been added to your cart. Redirecting...', 'learnpress' ),
696 + $course->get_title()
697 + );
698 + $response->data->redirect = esc_url_raw( $redirect_url );
699 + } catch ( Exception $e ) {
700 + $response->message = $e->getMessage();
701 + }
702 +
703 + return rest_ensure_response( $response );
704 + }
705 +
706 + /**
707 + * Rest API for retake course.
708 + *
709 + * @param WP_REST_Request $request .
710 + *
711 + * @throws Exception .
712 + */
713 + public function retake_course( WP_REST_Request $request ) {
714 + $response = new LP_REST_Response();
715 +
716 + try {
717 + $course_id = $request->get_param( 'id' );
718 +
719 + if ( ! $course_id ) {
720 + throw new Exception( __( 'Invalid params', 'learnpress' ) );
721 + }
722 +
723 + $courseModel = CourseModel::find( $course_id, true );
724 + if ( ! $courseModel ) {
725 + throw new Exception( __( 'Invalid course', 'learnpress' ) );
726 + }
727 +
728 + $userModel = UserModel::find( get_current_user_id(), true );
729 + if ( ! $userModel ) {
730 + throw new Exception( __( 'Invalid user', 'learnpress' ) );
731 + }
732 +
733 + $userCourseModel = UserCourseModel::find( $userModel->get_id(), $courseModel->get_id(), true );
734 + if ( ! $userCourseModel ) {
735 + throw new Exception( __( 'Invalid user course', 'learnpress' ) );
736 + }
737 +
738 + $userCourseModel->handle_retake();
739 + $item_continue = $userCourseModel->get_item_continue();
740 + if ( $item_continue ) {
741 + $link_continue = $courseModel->get_item_link( $item_continue->ID );
742 + } else {
743 + $link_continue = $courseModel->get_permalink();
744 + }
745 +
746 + $response->status = 'success';
747 + $response->message = esc_html__( 'Now you can begin this course', 'learnpress' );
748 + $response->data->url_redirect = $link_continue;
749 + } catch ( Exception $e ) {
750 + $response->message = $e->getMessage();
751 + }
752 +
753 + return $response;
754 + }
755 +
756 + /**
757 + * @param WP_REST_Request $request
758 + *
759 + * @return WP_REST_Response
760 + */
761 + public function get_items( $request ) {
762 + $settings = LP_Settings::instance();
763 + $response = array(
764 + 'result' => $settings->get(),
765 + );
766 +
767 + return rest_ensure_response( $response );
768 + }
769 +
770 + /**
771 + * @param WP_REST_Request $request
772 + *
773 + * @return WP_REST_Response
774 + */
775 + public function get_item( $request ) {
776 + $settings = LP_Settings::instance();
777 + $response = array(
778 + 'result' => $settings->get( $request['key'] ),
779 + );
780 +
781 + return rest_ensure_response( $response );
782 + }
783 +
784 + /**
785 + * @param WP_REST_Request $request
786 + *
787 + * @return WP_REST_Response
788 + */
789 + public function update_item( $request ) {
790 + $response = array();
791 + $settings = LP_Settings::instance();
792 + $option = $settings->get( $request['key'] );
793 +
794 + $settings->update( $request['key'], $request['data'] );
795 + $new_option = $settings->get( $request['key'] );
796 + $success = maybe_serialize( $option ) !== maybe_serialize( $new_option );
797 +
798 + $response['success'] = $success;
799 + $response['result'] = $success ? $new_option : $option;
800 +
801 + return rest_ensure_response( $response );
802 + }
803 +
804 + /**
805 + * @param WP_REST_Request $request
806 + *
807 + * @return WP_REST_Response
808 + */
809 + public function delete_item( $request ) {
810 + $response = array();
811 +
812 + return rest_ensure_response( $response );
813 + }
814 +
815 + /**
816 + * Rest API for get item continue in single course.
817 + *
818 + * @param WP_REST_Request $request
819 + *
820 + * @return LP_REST_Response
821 + * @since 4.1.4
822 + * @version 1.0.5
823 + */
824 + public function continue_course( WP_REST_Request $request ): LP_REST_Response {
825 + $params = $request->get_params();
826 + $response = new LP_REST_Response();
827 + $response->data = '';
828 +
829 + try {
830 + $flag_found = false;
831 + $item_link = '';
832 + $course_id = absint( $params['courseId'] ?? 0 );
833 + $user_id = get_current_user_id();
834 +
835 + $user = UserModel::find( $user_id, true );
836 + $course = CourseModel::find( $course_id, true );
837 + if ( ! $course ) {
838 + throw new Exception( __( 'Invalid course', 'learnpress' ) );
839 + }
840 +
841 + if ( ! $user ) {
842 + return $response;
843 + }
844 +
845 + $userCourseModel = UserCourseModel::find( $user_id, $course_id, true );
846 + if ( ! $userCourseModel ) {
847 + throw new Exception( __( 'Invalid user course', 'learnpress' ) );
848 + }
849 +
850 + $sections_items = $course->get_section_items();
851 + $total_items = $course->get_total_items();
852 +
853 + if ( ! empty( $total_items ) ) {
854 + foreach ( $sections_items as $section_items ) {
855 + if ( $flag_found ) {
856 + break;
857 + }
858 +
859 + foreach ( $section_items->items as $item ) {
860 + $item_id = $item->id ?? $item->item_id;
861 + $item_type = $item->type ?? $item->item_type;
862 +
863 + $userItemModel = UserItemModel::find_user_item(
864 + $user_id,
865 + $item_id,
866 + $item_type,
867 + $course_id,
868 + LP_COURSE_CPT,
869 + true
870 + );
871 + if ( ! $userItemModel || $userItemModel->get_status() !== LP_ITEM_COMPLETED ) {
872 + $item_link = $course->get_item_link( $item->id );
873 + $flag_found = true;
874 + break;
875 + }
876 + }
877 + }
878 +
879 + if ( ! $flag_found ) {
880 + $item_link = $course->get_item_link( $course->get_first_item_id() );
881 + }
882 + }
883 +
884 + $response->data = $item_link;
885 + $response->status = 'success';
886 + $response->message = '';
887 + } catch ( Exception $e ) {
888 + $response->message = $e->getMessage();
889 + }
890 +
891 + return $response;
892 + }
893 +}