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.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 4.1.7.3.1 All 141 releases
← All changes | inc/rest-api/v1/frontend/class-lp-rest-courses-controller.php +480 -398 4.1.7 → 4.4.9 View file →
@@ -2,8 +2,23 @@
2 2
3 3 /**
4 4 * Class LP_REST_Courses_Controller
5 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 +
6 21 class LP_REST_Courses_Controller extends LP_Abstract_REST_Controller {
7 22 /**
8 23 * LP_REST_Courses_Controller constructor.
9 24 */
@@ -17,16 +32,38 @@
17 32 * Register routes API
18 33 */
19 34 public function register_routes() {
20 35 $this->routes = array(
21 - 'purchase-course' => array(
36 + '' => array(
22 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(
23 60 'methods' => WP_REST_Server::CREATABLE,
24 61 'callback' => array( $this, 'purchase_course' ),
25 62 'permission_callback' => '__return_true',
26 63 ),
27 64 ),
28 - 'enroll-course' => array(
65 + 'enroll-course' => array(
29 66 array(
30 67 'methods' => WP_REST_Server::CREATABLE,
31 68 'callback' => array( $this, 'enroll_courses' ),
32 69 'permission_callback' => '__return_true',
@@ -31,9 +68,9 @@
31 68 'callback' => array( $this, 'enroll_courses' ),
32 69 'permission_callback' => '__return_true',
33 70 ),
34 71 ),
35 - 'retake-course' => array(
72 + 'retake-course' => array(
36 73 array(
37 74 'methods' => WP_REST_Server::CREATABLE,
38 75 'callback' => array( $this, 'retake_course' ),
39 76 'permission_callback' => function () {
@@ -43,9 +80,9 @@
43 80 'type' => 'int',
44 81 ),
45 82 ),
46 83 ),
47 - 'archive-course' => array(
84 + /*'archive-course' => array(
48 85 array(
49 86 'methods' => WP_REST_Server::ALLMETHODS,
50 87 'callback' => array( $this, 'list_courses' ),
51 88 'permission_callback' => '__return_true',
@@ -50,13 +87,21 @@
50 87 'callback' => array( $this, 'list_courses' ),
51 88 'permission_callback' => '__return_true',
52 89 'args' => [],
53 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 + ),
54 99 ),
55 - '(?P<key>[\w]+)' => array(
100 + '(?P<key>[\w]+)' => array(
56 101 'args' => array(
57 102 'id' => array(
58 - 'description' => __( 'Unique identifier for the resource.', 'learnpress' ),
103 + 'description' => __( 'A unique identifier for the resource.', 'learnpress' ),
59 104 'type' => 'string',
60 105 ),
61 106 ),
62 107 array(
@@ -76,15 +121,13 @@
76 121 'permission_callback' => array( $this, 'check_admin_permission' ),
77 122 ),
78 123 'schema' => array( $this, 'get_public_item_schema' ),
79 124 ),
80 - 'continue-course' => array(
125 + 'continue-course' => array(
81 126 array(
82 127 'methods' => WP_REST_Server::CREATABLE,
83 128 'callback' => array( $this, 'continue_course' ),
84 - 'permission_callback' => function () {
85 - return is_user_logged_in();
86 - },
129 + 'permission_callback' => '__return_true',
87 130 ),
88 131 ),
89 132 );
90 133
@@ -100,141 +143,270 @@
100 143 return LP_Abstract_API::check_admin_permission();
101 144 }
102 145
103 146 /**
104 - * Get list courses
147 + * Get list courses, return JSON data, not to handle HTML
105 148 *
106 149 * @param WP_REST_Request $request
107 150 *
108 - * @return LP_REST_Response
151 + * @return Response
152 + * @since 4.2.8.2
153 + * @version 1.0.1
109 154 */
110 - public function list_courses( WP_REST_Request $request ): LP_REST_Response {
111 - $response = new LP_REST_Response();
112 - $response->data = new stdClass();
155 + public function get_courses( WP_REST_Request $request ): Response {
156 + $response = new Response();
113 157
114 158 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;
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;
123 194 }
124 - if ( ! empty( $fields_exclude_str ) ) {
125 - $fields_exclude = explode( ',', $fields_exclude_str );
126 - $filter->exclude_fields = $fields_exclude;
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 );
127 255 }
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;
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;
133 296 }
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 297
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' : '';
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 + }
144 311
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 - }
312 + return apply_filters( 'lp/rest-api/courses-search-suggest/response', $response );
313 + }
152 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 +
153 347 $total_rows = 0;
154 - $filter = apply_filters( 'lp/api/courses/filter', $filter, $request );
155 - $courses = LP_Course::get_courses( $filter, $total_rows );
348 + $courses = CourseService::get_list_courses( $filter, $total_rows );
156 349 $total_pages = LP_Database::get_total_pages( $filter->limit, $total_rows );
157 -
350 + $return_type = $request['return_type'] ?? 'html';
158 351 if ( 'json' === $return_type ) {
159 352 $response->data->courses = $courses;
160 353 $response->data->total_pages = $total_pages;
161 354 } else {
162 355 // For return data has html
356 + ob_start();
163 357 if ( $courses ) {
164 - global $wp, $post;
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;
165 367
166 - // Pagination
167 - $archive_link = get_post_type_archive_link( LP_COURSE_CPT );
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
168 391
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;
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' ) );
177 396 } 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 );
397 + foreach ( $courses as $course ) {
398 + $post = get_post( $course->ID );
399 + setup_postdata( $post );
400 + Template::instance()->get_frontend_template( 'content-course.php' );
209 401 }
210 402
211 - $template_path_item = urldecode( $template_path_item );
403 + wp_reset_postdata();
212 404 }
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();
405 + // End content items
233 406 }
234 - // End content items
235 407 } else {
236 - LP()->template( 'course' )->no_courses_found();
408 + LearnPress::instance()->template( 'course' )->no_courses_found();
237 409 }
238 410
239 411 $response->data->content = ob_get_clean();
240 412 $response->data->totals = $total_rows;
@@ -249,9 +421,21 @@
249 421 } else {
250 422 if ( $from == $to ) {
251 423 $response->data->from_to = sprintf( esc_html__( 'Showing last course of %s results', 'learnpress' ), $total_rows );
252 424 } else {
253 - $from_to = $from . '-' . $to;
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 +
254 438 $response->data->from_to = sprintf( esc_html__( 'Showing %1$s of %2$s results', 'learnpress' ), $from_to, $total_rows );
255 439 }
256 440 }
257 441 }
@@ -257,193 +441,115 @@
257 441 }
258 442
259 443 $response->status = 'success';
260 444 } catch ( Throwable $e ) {
261 - $response->message = $e->getMessage();
445 + $response->data->content = $e->getMessage();
446 + $response->message = $e->getMessage();
262 447 }
263 448
264 449 return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
265 450 }
266 451
267 - /*public function archive_course( WP_REST_Request $request ) {
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 {
268 460 $response = new LP_REST_Response();
269 461 $response->data = new stdClass();
270 462
271 - $s = isset( $request['s'] ) ? sanitize_text_field( $request['s'] ) : false;
272 - $page = isset( $request['paged'] ) ? absint( wp_unslash( $request['paged'] ) ) : 1;
273 - $order = isset( $request['order'] ) ? wp_unslash( $request['order'] ) : false;
274 - $orderby = isset( $request['orderby'] ) ? wp_unslash( $request['orderby'] ) : false;
275 - $taxonomy = isset( $request['taxonomy'] ) ? wp_unslash( $request['taxonomy'] ) : false;
276 - $term_id = isset( $request['term_id'] ) ? wp_unslash( $request['term_id'] ) : false;
277 - $user_id = isset( $request['userID'] ) ? absint( wp_unslash( $request['userID'] ) ) : false;
278 - $limit = LP_Settings::get_option( 'archive_course_limit', -1 );
279 -
280 - $args = array(
281 - 'posts_per_page' => $limit,
282 - 'paged' => $page,
283 - 'post_type' => LP_COURSE_CPT,
284 - );
285 -
286 - if ( ! empty( $s ) ) {
287 - $args['s'] = $s;
288 - }
289 -
290 - if ( ! empty( $taxonomy ) && ! empty( $term_id ) ) {
291 - $args['tax_query'] = array(
292 - array(
293 - 'taxonomy' => $taxonomy,
294 - 'field' => 'term_id',
295 - 'terms' => $term_id,
296 - ),
463 + try {
464 + $settings = array_merge(
465 + $request->get_params(),
466 + [
467 + 'courses_ul_classes' => [ 'list-courses-elm' ],
468 + ]
297 469 );
470 + $response->data->content = ListCoursesByPageElementor::render_data_from_setting( $settings );
298 471
299 - $term_link = get_term_link( $term_id, $taxonomy );
472 + $response->status = 'success';
473 + } catch ( Throwable $e ) {
474 + ob_end_clean();
475 + $response->data->content = $e->getMessage();
476 + $response->message = $e->getMessage();
300 477 }
301 478
302 - if ( ! empty( $order ) ) {
303 - $args['order'] = $order;
304 - }
479 + return apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response );
480 + }
305 481
306 - if ( ! empty( $orderby ) ) {
307 - $args['orderby'] = $orderby;
308 - }
309 -
310 - if ( $user_id && learn_press_user_maybe_is_a_teacher( $user_id ) ) {
311 - $args['post_status'] = array( 'publish', 'private' );
312 - }
313 -
314 - $args = apply_filters( 'lp/rest-api/frontend/course/archive_course/query_args', $args, $request );
315 -
316 - $query = new WP_Query( $args );
317 -
318 - $num_pages = ! empty( $query->max_num_pages ) ? $query->max_num_pages : 1;
319 -
320 - $archive_link = get_post_type_archive_link( LP_COURSE_CPT );
321 -
322 - if ( isset( $term_link ) && ! is_wp_error( $term_link ) ) {
323 - $archive_link = $term_link;
324 - }
325 -
326 - $base = esc_url_raw( str_replace( 999999999, '%#%', get_pagenum_link( 999999999, false ) ) );
327 -
328 - global $wp;
329 - $base = str_replace( home_url( $wp->request ) . '/', $archive_link, $base );
330 -
331 - $response->data->pagination = learn_press_get_template_content(
332 - 'loop/course/pagination.php',
333 - array(
334 - 'total' => $num_pages,
335 - 'paged' => $page,
336 - 'base' => $base,
337 - )
338 - );
339 -
340 - ob_start();
341 -
342 - if ( $query->have_posts() ) {
343 - global $post;
344 -
345 - while ( $query->have_posts() ) {
346 - $query->the_post();
347 - learn_press_get_template_part( 'content', 'course' );
348 - }
349 -
350 - wp_reset_postdata();
351 - } else {
352 - LP()->template( 'course' )->no_courses_found();
353 - }
354 -
355 - $response->status = 'success';
356 - $response->data->content = ob_get_clean();
357 -
358 - return rest_ensure_response( apply_filters( 'lp/rest-api/frontend/course/archive_course/response', $response ) );
359 - }*/
360 -
361 482 /**
362 483 * Rest API for Enroll in single course.
363 484 *
364 485 * @param WP_REST_Request $request
365 486 *
366 - * @throws Exception .
487 + * @return LP_REST_Response
367 488 * @author Nhamdv
368 489 * @editor tungnx
369 - * @version 1.0.1
490 + * @version 1.0.3
370 491 * @since 4.0.0
371 - * @modify 4.1.2
372 492 */
373 - public function enroll_courses( WP_REST_Request $request ) {
374 - $response = new LP_REST_Response();
375 - $response->data = new stdClass();
376 - $lp_user_items_db = LP_User_Items_DB::getInstance();
493 + public function enroll_courses( WP_REST_Request $request ): LP_REST_Response {
494 + $response = new LP_REST_Response();
495 + $response->data = new stdClass();
377 496
378 497 try {
379 - if ( empty( absint( $request['id'] ) ) ) {
380 - throw new Exception( esc_html__( 'Error: No course available!.', 'learnpress' ) );
381 - }
382 -
383 - $course_id = absint( $request['id'] );
384 - $course = learn_press_get_course( $course_id );
385 - $user = learn_press_get_current_user();
386 -
498 + $course_id = absint( $request['id'] ?? 0 );
499 + $course = CourseModel::find( $course_id, true );
387 500 if ( ! $course ) {
388 501 throw new Exception( esc_html__( 'Invalid course!', 'learnpress' ) );
389 502 }
390 503
391 - $can_enroll = $user->can_enroll_course( $course_id, false );
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 + }
392 510
393 - if ( ! $can_enroll->check ) {
394 - throw new Exception( $can_enroll->message ?? esc_html__( 'Error: Cannot enroll course.', 'learnpress' ) );
395 - }
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();
396 518
397 - $filter = new LP_User_Items_Filter();
398 - $filter->user_id = get_current_user_id();
399 - $filter->item_id = $course_id;
400 - $course_item = $lp_user_items_db->get_last_user_course( $filter );
519 + do_action( 'learnpress/user/course-enrolled', $userCourse->ref_id, $course_id, $user->get_id() );
401 520
402 - // Case: if user bought course - or create order manual with order "completed".
403 - if ( $course_item && 'purchased' == $course_item->status ) {
404 - $user_item_data = [
405 - 'user_item_id' => $course_item->user_item_id,
406 - 'graduation' => LP_COURSE_GRADUATION_IN_PROGRESS,
407 - 'status' => LP_COURSE_ENROLLED,
408 - 'start_time' => current_time( 'mysql', true ),
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(),
409 529 ];
410 -
411 - $user_item_new_or_update = new LP_User_Item_Course( $user_item_data );
412 - $result = $user_item_new_or_update->update();
413 -
414 - if ( ! $result ) {
415 - throw new Exception( esc_html__( 'Error: Can\'t Enroll course.', 'learnpress' ) );
416 - }
417 -
418 - do_action( 'learnpress/user/course-enrolled', $course_item->ref_id, $course_id, $user->get_id() );
530 + LPBackgroundAjax::handle(
531 + [
532 + 'params' => $data_send,
533 + 'lp-load-ajax' => 'send_mail_user_enrolled_course',
534 + ]
535 + );
419 536 } else { // Case enroll course free
420 - LP()->session->set( 'order_awaiting_payment', '' );
421 -
422 - $cart = LP()->cart;
537 + $cart = LearnPress::instance()->cart;
423 538 $checkout = LP_Checkout::instance();
424 539
425 540 if ( ! learn_press_enable_cart() ) {
426 - $order_awaiting_payment = LP()->session->order_awaiting_payment;
427 541 $cart->empty_cart();
428 - LP()->session->order_awaiting_payment = $order_awaiting_payment;
429 542 }
430 543
431 - $cart_id = $cart->add_to_cart( $course_id, 1, array() );
432 -
544 + $cart_id = $cart->add_to_cart( $course_id );
433 545 if ( ! $cart_id ) {
434 - throw new Exception( esc_html__( 'Error: Can\'t add Course to cart.', 'learnpress' ) );
546 + throw new Exception( esc_html__( 'Error: The course cannot be added to the cart.', 'learnpress' ) );
435 547 }
436 548
437 549 if ( is_user_logged_in() ) {
438 550 $order_id = $checkout->create_order();
439 -
440 - if ( is_wp_error( $order_id ) ) {
441 - throw new Exception( $order_id->get_error_message() );
442 - }
443 -
444 - $order = new LP_Order( $order_id );
445 -
551 + $order = new LP_Order( $order_id );
446 552 $order->payment_complete();
447 553
448 554 $cart->empty_cart();
449 555 }
@@ -449,41 +555,38 @@
449 555 }
450 556 }
451 557
452 558 if ( is_user_logged_in() ) {
453 - $response->status = 'success';
454 - // Course has no items
455 559 $response->message = esc_html__(
456 - 'Congrats! You enroll course successfully. Redirecting...',
560 + 'Congrats! You have enrolled in the course successfully. Redirecting...',
457 561 'learnpress'
458 562 );
459 563
460 - $response->data->redirect = $course->get_redirect_url_after_enroll();
461 -
462 - if ( empty( $course->count_items() ) ) {
463 - $response->data->redirect = get_permalink( $course->get_id() );
464 - }
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() );
465 566 } else {
567 + $redirect_url = LP_Page_Controller::get_link_page( 'checkout', [], true );
466 568 $redirect_url = apply_filters(
467 569 'learnpress/rest-api/courses/enroll/redirect',
468 - learn_press_get_page_link( 'checkout' ),
570 + $redirect_url,
469 571 $course_id
470 572 );
471 573
472 574 if ( empty( $redirect_url ) ) {
473 - throw new Exception( __( 'Error: Please setup page for checkout.', 'learnpress' ) );
474 - } elseif ( ! is_user_logged_in() ) { // Fix case: cache page with user anonymous
475 - $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
575 + throw new Exception( __( 'Error: Please set up a page for checkout.', 'learnpress' ) );
476 576 }
477 577
578 + $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
478 579 $response->message = esc_html__( 'Redirecting...', 'learnpress' );
479 - $response->data->redirect = $redirect_url;
580 + $response->data->redirect = esc_url_raw( $redirect_url );
480 581 }
582 +
583 + $response->status = 'success';
481 584 } catch ( Exception $e ) {
482 585 $response->message = $e->getMessage();
483 586 }
484 587
485 - wp_send_json( $response );
588 + return $response;
486 589 }
487 590
488 591 /**
489 592 * Rest API for Purchase course in single course.
@@ -494,12 +597,11 @@
494 597 * @throws Exception .
495 598 * @author Nhamdv
496 599 */
497 600 public function purchase_course( WP_REST_Request $request ) {
498 - $response = new LP_REST_Response();
499 - $response->data = new stdClass();
500 - $params = $request->get_params();
501 - $lp_user_items_db = LP_User_Items_DB::getInstance();
601 + $response = new LP_REST_Response();
602 + $response->data = new stdClass();
603 + $params = $request->get_params();
502 604
503 605 try {
504 606 $course_id = $params['id'];
505 607 $allow_repurchase_type = $params['repurchaseType'] ?? false;
@@ -507,120 +609,94 @@
507 609 if ( ! $course_id ) {
508 610 throw new Exception( __( 'Error: Invalid Course ID.', 'learnpress' ) );
509 611 }
510 612
511 - $course = learn_press_get_course( $course_id );
512 -
613 + $course = CourseModel::find( $course_id, true );
513 614 if ( ! $course ) {
514 615 throw new Exception( __( 'Error: No Course available.', 'learnpress' ) );
515 616 }
516 617
517 - $user = learn_press_get_current_user();
518 -
519 - if ( ! $user->can_purchase_course( $course_id ) ) {
520 - throw new Exception( esc_html__( 'Error: Cannot purchase course!.', 'learnpress' ) );
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() );
521 623 }
522 624
523 - // Allow Repurchase.
524 - /*$latest_user_item_id = $wpdb->get_var(
525 - $wpdb->prepare(
526 - "SELECT MAX(user_item_id) user_item_id
527 - FROM {$wpdb->learnpress_user_items}
528 - WHERE ref_type = %s
529 - AND item_type = %s
530 - AND item_id = %d
531 - AND user_id = %d",
532 - LP_ORDER_CPT,
533 - LP_COURSE_CPT,
534 - $course_id,
535 - $user->get_id()
536 - )
537 - );*/
538 - $latest_user_item_id = 0;
539 -
540 - $filter = new LP_User_Items_Filter();
541 - $filter->user_id = get_current_user_id();
542 - $filter->item_id = $course_id;
543 - $course_item = $lp_user_items_db->get_last_user_course( $filter );
544 -
545 - if ( $course_item && isset( $course_item->user_item_id ) ) {
546 - $latest_user_item_id = $course_item->user_item_id;
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 + );
547 640 }
548 641
549 - if ( $course->allow_repurchase() && ! empty( $latest_user_item_id ) && empty( $allow_repurchase_type ) ) {
550 - if ( $course->allow_repurchase_course_option() === 'popup' ) {
551 - ob_start();
552 - ?>
553 - <div class="lp_allow_repuchase_select">
554 - <ul>
555 - <li>
556 - <label>
557 - <input name="_lp_allow_repurchase_type" value="reset" type="radio" checked="checked" />
558 - <?php esc_html_e( 'Reset Course progress', 'learnpress' ); ?>
559 - </label>
560 - </li>
561 - <li>
562 - <label>
563 - <input name="_lp_allow_repurchase_type" value="keep" type="radio" />
564 - <?php esc_html_e( 'Continue Course progress', 'learnpress' ); ?>
565 - </label>
566 - </li>
567 - </ul>
568 - </div>
569 - <?php
570 - $response->data->html = ob_get_clean();
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 );
571 655 $response->data->type = 'allow_repurchase';
572 656 $response->data->titlePopup = esc_html__( 'Repurchase Options', 'learnpress' );
573 657 $response->status = 'success';
574 658
575 659 return rest_ensure_response( $response );
576 - } else {
577 - learn_press_update_user_item_meta( $latest_user_item_id, '_lp_allow_repurchase_type', $course->allow_repurchase_course_option() );
578 660 }
579 661 }
580 662
581 - LP()->session->set( 'order_awaiting_payment', '' );
582 -
583 - $cart = LP()->cart;
584 - $checkout = LP_Checkout::instance();
585 -
663 + $cart = LearnPress::instance()->cart;
586 664 if ( ! learn_press_enable_cart() ) {
587 - $order_awaiting_payment = LP()->session->order_awaiting_payment;
588 665 $cart->empty_cart();
589 - LP()->session->order_awaiting_payment = $order_awaiting_payment;
590 666 }
591 667
592 - do_action( 'learnpress/rest-api/courses/purchase/before-add-to-cart' );
668 + // @deprecated hook since v4.2.7.3
669 + //do_action( 'learnpress/rest-api/courses/purchase/before-add-to-cart' );
593 670
594 - $cart_id = $cart->add_to_cart( $course_id, 1, array() );
595 -
596 - if ( ! $cart_id ) {
597 - throw new Exception( __( 'Error: Can\'t add Course to cart.', 'learnpress' ) );
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' ) );
598 674 }
599 675
600 - if ( ! empty( $allow_repurchase_type ) ) {
601 - learn_press_update_user_item_meta( $latest_user_item_id, '_lp_allow_repurchase_type', $allow_repurchase_type );
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 );
602 678 }
603 679
680 + $redirect_url = LP_Page_Controller::get_link_page( 'checkout', [], true );
604 681 $redirect_url = apply_filters(
605 682 'learnpress/rest-api/courses/purchase/redirect',
606 - learn_press_get_page_link( 'checkout' ),
683 + $redirect_url,
607 684 $course_id,
608 685 $cart_id
609 686 );
610 687
611 688 if ( empty( $redirect_url ) ) {
612 - throw new Exception( __( 'Error: Please setup page for checkout.', 'learnpress' ) );
613 - } elseif ( ! is_user_logged_in() ) { // Fix case: cache page with user anonymous
614 - $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
689 + throw new Exception( __( 'Error: Please set up a page for checkout.', 'learnpress' ) );
615 690 }
616 691
692 + $redirect_url = LP_Helper::get_link_no_cache( $redirect_url );
617 693 $response->status = 'success';
618 694 $response->message = sprintf(
619 - esc_html__( '"%s" has been added to your cart.', 'learnpress' ),
695 + esc_html__( '"%s" has been added to your cart. Redirecting...', 'learnpress' ),
620 696 $course->get_title()
621 697 );
622 - $response->data->redirect = $redirect_url;
698 + $response->data->redirect = esc_url_raw( $redirect_url );
623 699 } catch ( Exception $e ) {
624 700 $response->message = $e->getMessage();
625 701 }
626 702
@@ -643,56 +719,39 @@
643 719 if ( ! $course_id ) {
644 720 throw new Exception( __( 'Invalid params', 'learnpress' ) );
645 721 }
646 722
647 - $course = learn_press_get_course( $course_id );
648 -
649 - if ( ! $course ) {
723 + $courseModel = CourseModel::find( $course_id, true );
724 + if ( ! $courseModel ) {
650 725 throw new Exception( __( 'Invalid course', 'learnpress' ) );
651 726 }
652 727
653 - $user = learn_press_get_current_user();
728 + $userModel = UserModel::find( get_current_user_id(), true );
729 + if ( ! $userModel ) {
730 + throw new Exception( __( 'Invalid user', 'learnpress' ) );
731 + }
654 732
655 - // if ( ! is_user_logged_in() ) {
656 - // throw new Exception( esc_html__( 'Please login!', 'learnpress' ) );
657 - // }
733 + $userCourseModel = UserCourseModel::find( $userModel->get_id(), $courseModel->get_id(), true );
734 + if ( ! $userCourseModel ) {
735 + throw new Exception( __( 'Invalid user course', 'learnpress' ) );
736 + }
658 737
659 - $can_retry = $user->can_retry_course( $course_id );
660 -
661 - if ( ! $can_retry ) {
662 - throw new Exception( __( 'You can\'t retry course', 'learnpress' ) );
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();
663 744 }
664 745
665 - $user_course_data = $user->get_course_data( $course_id );
666 -
667 - // Up retaken.
668 - $user_course_data->increase_retake_count();
669 -
670 - // Set status, start_time, end_time of course to enrolled.
671 - $user_course_data->set_status( LP_COURSE_ENROLLED )
672 - ->set_start_time( current_time( 'mysql', true ) )
673 - ->set_end_time( '' )
674 - ->set_graduation( 'in-progress' )
675 - ->update();
676 -
677 - // Remove items' course user learned.
678 - $filter_remove = new LP_User_Items_Filter();
679 - $filter_remove->parent_id = $user_course_data->get_user_item_id();
680 - $filter_remove->user_id = $user_course_data->get_user_id();
681 - $filter_remove->limit = - 1;
682 - LP_User_Items_DB::getInstance()->remove_items_of_user_course( $filter_remove );
683 -
684 - // Create new result in table learnpress_user_item_results.
685 - LP_User_Items_Result_DB::instance()->insert( $user_course_data->get_user_item_id() );
686 -
687 746 $response->status = 'success';
688 - $response->message = esc_html__( 'Now you can learn this course', 'learnpress' );
689 - $response->data->url_redirect = $course->get_redirect_url_after_enroll();
747 + $response->message = esc_html__( 'Now you can begin this course', 'learnpress' );
748 + $response->data->url_redirect = $link_continue;
690 749 } catch ( Exception $e ) {
691 750 $response->message = $e->getMessage();
692 751 }
693 752
694 - wp_send_json( $response );
753 + return $response;
695 754 }
696 755
697 756 /**
698 757 * @param WP_REST_Request $request
@@ -753,17 +812,17 @@
753 812 return rest_ensure_response( $response );
754 813 }
755 814
756 815 /**
757 - * Rest API for Continue in single course.
816 + * Rest API for get item continue in single course.
758 817 *
759 818 * @param WP_REST_Request $request
760 - * @author minhpd
761 - * @editor tungnx
819 + *
820 + * @return LP_REST_Response
762 821 * @since 4.1.4
763 - * @version 1.0.1
822 + * @version 1.0.5
764 823 */
765 - public function continue_course( WP_REST_Request $request ) {
824 + public function continue_course( WP_REST_Request $request ): LP_REST_Response {
766 825 $params = $request->get_params();
767 826 $response = new LP_REST_Response();
768 827 $response->data = '';
769 828
@@ -770,15 +829,28 @@
770 829 try {
771 830 $flag_found = false;
772 831 $item_link = '';
773 832 $course_id = absint( $params['courseId'] ?? 0 );
774 - $user_id = absint( $params['userId'] ?? 0 );
833 + $user_id = get_current_user_id();
775 834
776 - $user = learn_press_get_user( $user_id );
777 - $course = learn_press_get_course( $course_id );
778 - $sections_items = $course->get_full_sections_and_items_course();
779 - $total_items = $course->count_items();
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 + }
780 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 +
781 853 if ( ! empty( $total_items ) ) {
782 854 foreach ( $sections_items as $section_items ) {
783 855 if ( $flag_found ) {
784 856 break;
@@ -784,9 +856,20 @@
784 856 break;
785 857 }
786 858
787 859 foreach ( $section_items->items as $item ) {
788 - if ( ! $user->has_completed_item( $item->id, $course_id ) ) {
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 ) {
789 872 $item_link = $course->get_item_link( $item->id );
790 873 $flag_found = true;
791 874 break;
792 875 }
@@ -804,8 +887,7 @@
804 887 } catch ( Exception $e ) {
805 888 $response->message = $e->getMessage();
806 889 }
807 890
808 - wp_send_json( $response );
891 + return $response;
809 892 }
810 -
811 893 }