PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.8
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
← All changes | inc/course/class-lp-course.php +1101 -1101 4.3.9.14.4.8 View file →
@@ -1,1101 +1,1101 @@
1 -<?php
2 -/**
3 - * Class LP_Course.
4 - *
5 - * @author ThimPress
6 - * @package LearnPress/Classes
7 - * @version 4.0.1
8 - */
9 -
10 -use LearnPress\Models\CourseModel;
11 -use LearnPress\Models\CoursePostModel;
12 -
13 -defined( 'ABSPATH' ) || exit();
14 -
15 -if ( ! class_exists( 'LP_Course' ) ) {
16 -
17 - /**
18 - * Class LP_Course
19 - */
20 - class LP_Course extends LP_Abstract_Course {
21 - protected $key_info_extra_fast_query = '_lp_info_extra_fast_query';
22 -
23 - /**
24 - * LP_Course constructor.
25 - *
26 - * @param mixed $course
27 - */
28 - public function __construct( $course ) {
29 - parent::__construct( $course );
30 - }
31 -
32 - /**
33 - * Debug log.
34 - *
35 - * @param $data
36 - *
37 - * @return array
38 - * @editor tungnx
39 - * @modify 4.1.5 - comment - not use
40 - */
41 - /*public static function log( $data ) {
42 - return $data;
43 - }*/
44 -
45 - /**
46 - * Get default course meta.
47 - *
48 - * @return mixed
49 - * @since 3.0.0
50 - */
51 - public static function get_default_meta() {
52 - $meta = array(
53 - 'duration' => '10 weeks',
54 - 'max_students' => 1000,
55 - 'students' => 0,
56 - 'retake_count' => 0,
57 - 'featured' => 'no',
58 - //'block_lesson_content' => 'no',
59 - 'external_link_buy_course' => '',
60 - 'course_result' => 'evaluate_lesson',
61 - 'passing_condition' => 80,
62 - 'price' => '',
63 - 'sale_price' => '',
64 - 'sale_start' => '',
65 - 'sale_end' => '',
66 - 'required_enroll' => 'yes',
67 - 'course_author' => learn_press_get_current_user_id(),
68 - );
69 -
70 - return apply_filters( 'learn-press/course/default-meta', $meta );
71 - }
72 -
73 - /**
74 - * Get LP Course.
75 - *
76 - * @param int $course_id
77 - *
78 - * @return mixed|bool|LP_Course
79 - * @Todo - Tungnx review to rewrite this method.
80 - */
81 - public static function get_course( int $course_id = 0 ) {
82 - if ( isset( LP_Global::$courses[ $course_id ] ) ) {
83 - return LP_Global::$courses[ $course_id ];
84 - }
85 -
86 - $the_course = self::get_course_object( $course_id );
87 -
88 - if ( ! $the_course ) {
89 - return false;
90 - }
91 -
92 - $key_args = wp_parse_args(
93 - array(
94 - 'id' => $the_course->ID,
95 - 'type' => $the_course->post_type,
96 - )
97 - );
98 -
99 - $key = LP_Helper::array_to_md5( $key_args );
100 -
101 - if ( empty( LP_Global::$courses[ $key ] ) ) {
102 - $class_name = self::get_course_class( $the_course );
103 - if ( is_string( $class_name ) && class_exists( $class_name ) ) {
104 - $course = new $class_name( $the_course->ID );
105 - } elseif ( $class_name instanceof LP_Abstract_Course ) {
106 - $course = $class_name;
107 - } else {
108 - $course = new self( $the_course->ID );
109 - }
110 - LP_Global::$courses[ $key ] = $course;
111 - } else {
112 - $course = LP_Global::$courses[ $key ];
113 - }
114 -
115 - /**
116 - * Force to reload course data into cache if it is not
117 - * loaded or has been deleted for some reasons.
118 - */
119 - $course->load();
120 -
121 - return $course;
122 - }
123 -
124 - /**
125 - * @param string $course_type
126 - *
127 - * @return string|false
128 - */
129 - private static function get_class_name_from_course_type( $course_type ) {
130 - return LP_COURSE_CPT === $course_type ? __CLASS__ : 'LP_Course_' . implode(
131 - '_',
132 - array_map( 'ucfirst', explode( '-', $course_type ) )
133 - );
134 - }
135 -
136 - /**
137 - * Get the course class name
138 - *
139 - * @param WP_Post $the_course
140 - * @param array $args (default: array())
141 - *
142 - * @return string
143 - */
144 - private static function get_course_class( $the_course, $args = array() ) {
145 - $course_id = absint( $the_course->ID );
146 - $type = $the_course->post_type;
147 -
148 - $class_name = self::get_class_name_from_course_type( $type );
149 -
150 - // Filter class name so that the class can be overridden if extended.
151 - return apply_filters( 'learn-press/course/object-class', $class_name, $type, $course_id );
152 - }
153 -
154 - /**
155 - * Get the course object
156 - *
157 - * @param mixed $the_course
158 - *
159 - * @return WP_Post|bool false on failure
160 - * @uses WP_Post
161 - */
162 - private static function get_course_object( $the_course ) {
163 - $the_course_passed = $the_course;
164 - if ( false === $the_course ) {
165 - $the_course = get_post_type() === LP_COURSE_CPT ? $GLOBALS['post'] : false;
166 - } elseif ( is_numeric( $the_course ) ) {
167 - $the_course = get_post( $the_course );
168 - } elseif ( $the_course instanceof LP_Abstract_Course ) {
169 - $the_course = get_post( $the_course->get_id() );
170 - } elseif ( ! ( $the_course instanceof WP_Post ) ) {
171 - $the_course = false;
172 - }
173 -
174 - if ( $the_course && $the_course->post_type !== LP_COURSE_CPT ) {
175 - $the_course = false;
176 - }
177 -
178 - return apply_filters( 'learn-press/course/post-object', $the_course, $the_course_passed );
179 - }
180 -
181 - /**
182 - * Check time remaining course when enable duration expire
183 - * Value: -1 is no limit (default)
184 - * Value: 0 is block
185 - * Administrator || (is instructor && is author course) will be not block.
186 - *
187 - * @return int second
188 - * @since 4.0.0
189 - * @author tungnx
190 - * @version 1.0.1
191 - */
192 - public function timestamp_remaining_duration(): int {
193 - $timestamp_remaining = - 1;
194 - $user = learn_press_get_user( get_current_user_id() );
195 -
196 - if ( current_user_can( 'administrator' ) ||
197 - ( current_user_can( LP_TEACHER_ROLE ) && $this->get_author()->get_id() === $user->get_id() ) ) {
198 - return $timestamp_remaining;
199 - }
200 -
201 - if ( 0 === absint( $this->get_data( 'duration' ) ) ) {
202 - return $timestamp_remaining;
203 - }
204 -
205 - if ( 'yes' !== $this->get_data( 'block_course_duration_expire' ) ) {
206 - return $timestamp_remaining;
207 - }
208 -
209 - if ( $user instanceof LP_User_Guest ) {
210 - return $timestamp_remaining;
211 - }
212 -
213 - $course_item_data = $user->get_course_data( $this->get_id() );
214 -
215 - if ( ! $course_item_data ) {
216 - return $timestamp_remaining;
217 - }
218 -
219 - $course_start_time = $course_item_data->get_start_time()->get_raw_date();
220 - $duration = $this->get_data( 'duration' );
221 - $timestamp_expire = strtotime( $course_start_time . ' +' . $duration );
222 - $timestamp_current = time();
223 - $timestamp_remaining = $timestamp_expire - $timestamp_current;
224 -
225 - if ( $timestamp_remaining < 0 ) {
226 - $timestamp_remaining = 0;
227 - }
228 -
229 - return apply_filters( 'learnpress/course/block_duration_expire/timestamp_remaining', $timestamp_remaining );
230 - }
231 -
232 - /**
233 - * Get option enable block course when finish course
234 - *
235 - * @return bool
236 - */
237 - public function enable_block_item_when_finish(): bool {
238 - return 'yes' === $this->get_data( 'block_course_finished' );
239 - }
240 -
241 - public function allow_repurchase(): bool {
242 - return 'yes' === $this->get_data( 'allow_repurchase' );
243 - }
244 -
245 - public function allow_repurchase_course_option(): string {
246 - return $this->get_data( 'allow_repurchase_course_option', 'reset' );
247 - }
248 -
249 - /**
250 - * Get first item of course
251 - *
252 - * @author tungnx
253 - * @since 4.0.0
254 - * @modify 4.1.3
255 - * @version 1.0.2
256 - * @return int
257 - */
258 - public function get_first_item_id(): int {
259 - $course_id = $this->get_id();
260 -
261 - $courseModel = CourseModel::find( $course_id, true );
262 - return $courseModel->get_first_item_id();
263 -
264 - try {
265 - // Get cache
266 - $lp_course_cache = LP_Course_Cache::instance();
267 - $key_cache = "$course_id/first_item_id";
268 - $first_item_id = $lp_course_cache->get_cache( $key_cache );
269 -
270 - if ( ! $first_item_id ) {
271 - $extra_info = $this->get_info_extra_for_fast_query();
272 -
273 - if ( ! $extra_info->first_item_id ) {
274 - $first_item_id = LP_Course_DB::getInstance()->get_first_item_id( $course_id );
275 - $extra_info->first_item_id = $first_item_id;
276 -
277 - // Save post meta
278 - $this->set_info_extra_for_fast_query( $extra_info );
279 - } else {
280 - $first_item_id = $extra_info->first_item_id;
281 - }
282 -
283 - $lp_course_cache->set_cache( $key_cache, $first_item_id );
284 - }
285 - } catch ( Throwable $e ) {
286 - $first_item_id = 0;
287 - }
288 -
289 - return $first_item_id;
290 - }
291 -
292 - /**
293 - * Get redirect url after enroll course
294 - *
295 - * @author tungnx
296 - * @version 1.0.0
297 - * @since 4.0.0
298 - * @return false|string|WP_Error
299 - */
300 - public function get_redirect_url_after_enroll() {
301 - $first_item_id = $this->get_first_item_id();
302 - $redirect = $first_item_id ? $this->get_item_link( $first_item_id ) : get_the_permalink( $this->get_id() );
303 -
304 - return apply_filters( 'learnpress/rest-api/enroll-course/redirect', $redirect );
305 - }
306 -
307 - /**
308 - * Get info extra on post meta to query fast
309 - *
310 - * @since 4.1.3
311 - * @author tungnx
312 - * @version 1.0.0
313 - * @return LP_Course_Extra_Info_Fast_Query_Model
314 - * @deprecated 4.2.7.4
315 - */
316 - public function get_info_extra_for_fast_query(): LP_Course_Extra_Info_Fast_Query_Model {
317 - $extra_info = new LP_Course_Extra_Info_Fast_Query_Model();
318 -
319 - try {
320 - $extra_info_str = get_post_meta( $this->get_id(), $this->key_info_extra_fast_query, true );
321 -
322 - if ( ! empty( $extra_info_str ) ) {
323 - $extra_info_stdclass = LP_Helper::json_decode( $extra_info_str );
324 - $extra_info = $extra_info->map_stdclass( $extra_info_stdclass );
325 - }
326 - } catch ( Throwable $e ) {
327 - error_log( __METHOD__ . ': ' . $e->getMessage() );
328 - }
329 -
330 - return $extra_info;
331 - }
332 -
333 - /**
334 - * Set extra info for query fast on post meta
335 - *
336 - * @since 4.1.3
337 - * @author tungnx
338 - * @version 1.0.0
339 - * @param LP_Course_Extra_Info_Fast_Query_Model $data_object
340 - */
341 - public function set_info_extra_for_fast_query( LP_Course_Extra_Info_Fast_Query_Model $data_object ) {
342 - try {
343 - $extra_info_json = json_encode( $data_object, JSON_UNESCAPED_UNICODE );
344 -
345 - if ( JSON_ERROR_NONE !== json_last_error() ) {
346 - throw new Exception( 'Error encode on ' . __METHOD__ );
347 - }
348 -
349 - update_post_meta( $this->get_id(), $this->key_info_extra_fast_query, $extra_info_json );
350 - } catch ( Throwable $e ) {
351 - error_log( $e->getMessage() );
352 - }
353 - }
354 -
355 - /**
356 - * Get info total items of Course
357 - *
358 - * @param string $type
359 - * @param bool $include_preview
360 - *
361 - * @return int
362 - * @throws Exception
363 - * @version 1.0.1
364 - * @author tungnx
365 - * @since 4.1.4.1
366 - */
367 - public function count_items( string $type = '', bool $include_preview = true ): int {
368 - $course_id = $this->get_id();
369 - $courseModel = CourseModel::find( $course_id, true );
370 - if ( ! $courseModel ) {
371 - return 0;
372 - }
373 -
374 - $total_items = $courseModel->get_total_items();
375 - if ( empty( $total_items ) ) {
376 - return 0;
377 - }
378 -
379 - if ( ! empty( $type ) ) {
380 - if ( isset( $total_items->{$type} ) ) {
381 - return $total_items->{$type};
382 - }
383 - } else {
384 - return $total_items->count_items ?? 0;
385 - }
386 -
387 - return 0;
388 - }
389 -
390 - /**
391 - * Delete relate data when delete course
392 - *
393 - * @since 4.1.4.1
394 - * @author tungnx
395 - * @version 1.0.0
396 - */
397 - public function delete_relate_data_when_delete_course() {
398 - $lp_section_db = LP_Section_DB::getInstance();
399 - $lp_user_items_db = LP_User_Items_DB::getInstance();
400 -
401 - try {
402 - // Check valid user
403 - if ( ! current_user_can( 'administrator' ) && $this->get_author( 'id' ) !== get_current_user_id() ) {
404 - return;
405 - }
406 -
407 - $section_ids = $lp_section_db->get_section_ids_by_course( $this->get_id() );
408 -
409 - $filter = new LP_Section_Filter();
410 - $filter->section_ids = $section_ids;
411 - $filter->author_id_course = $this->get_author( 'id' );
412 -
413 - // Delete section
414 - $lp_section_db->delete_section( $filter );
415 - // Delete section items
416 - $lp_section_db->delete_section_items( $filter );
417 -
418 - $filter_user_items = new LP_User_Items_Filter();
419 - $filter_user_items->item_id = $this->get_id();
420 - $user_course_ids = $lp_user_items_db->get_user_items_by_course( $filter_user_items );
421 -
422 - $this->delete_user_item_and_result( $user_course_ids );
423 -
424 - // Clear cache total students enrolled.
425 - $lp_course_cache = new LP_Course_Cache( true );
426 - $lp_course_cache->clean_total_students_enrolled( $this->get_id() );
427 - $lp_course_cache->clean_total_students_enrolled_or_purchased( $this->get_id() );
428 - // Clear cache count students many courses.
429 - $lp_courses_cache = new LP_Courses_Cache( true );
430 - $lp_courses_cache->clear_cache_on_group( LP_Courses_Cache::KEYS_COUNT_STUDENT_COURSES );
431 - $lp_course_cache->clear_cache_on_group( LP_Courses_Cache::KEYS_COUNT_COURSES_FREE );
432 - // Clear cache user course.
433 - $lp_user_items_cache = new LP_User_Items_Cache();
434 - $lp_user_items_cache->clean_user_items_by_course( $this->get_id() );
435 - } catch ( Throwable $e ) {
436 - error_log( __FUNCTION__ . ':' . $e->getMessage() );
437 - }
438 - }
439 -
440 - /**
441 - * Delete user_items, user_itemmeta, user_item_results
442 - * WHERE IN user_item_ids
443 - *
444 - * @param array $user_course_ids
445 - */
446 - public function delete_user_item_and_result( array $user_course_ids ) {
447 - $lp_user_items_db = LP_User_Items_DB::getInstance();
448 - $lp_user_item_results = LP_User_Items_Result_DB::instance();
449 -
450 - try {
451 - if ( empty( $user_course_ids ) ) {
452 - return;
453 - }
454 -
455 - // Get user_item_ids has parent in $user_course_ids
456 - $filter = new LP_User_Items_Filter();
457 - $filter->user_item_ids = $user_course_ids;
458 - $user_item_ids = $lp_user_items_db->get_item_ids_of_user_course( $filter );
459 -
460 - $user_item_ids_concat = array_merge( $user_course_ids, $user_item_ids );
461 -
462 - // Delete on tb lp_user_items
463 - $filter_delete = new LP_User_Items_Filter();
464 - $filter_delete->user_item_ids = $user_item_ids_concat;
465 - $user_items_rs = $lp_user_items_db->get_user_items( $filter_delete );
466 -
467 - // For each to clear cache
468 - foreach ( $user_items_rs as $user_item ) {
469 - $lp_user_items_cache = new LP_User_Items_Cache();
470 - $key_one = "userItemModel/find/{$user_item->user_id}/{$user_item->item_id}/{$user_item->item_type}";
471 - $key_two = "userItemModel/find/{$user_item->user_id}/{$user_item->item_id}/{$user_item->item_type}/{$user_item->ref_id}/{$user_item->ref_type}";
472 - $lp_user_items_cache->clear( $key_one );
473 - $lp_user_items_cache->clear( $key_two );
474 - }
475 -
476 - $lp_user_items_db->remove_user_item_ids( $filter_delete );
477 -
478 - // Delete user_itemmeta
479 - $lp_user_items_db->remove_user_itemmeta( $filter_delete );
480 -
481 - // Delete user_item_results
482 - $lp_user_item_results->remove_user_item_results( $filter_delete );
483 - } catch ( Throwable $e ) {
484 - error_log( __FUNCTION__ . ':' . $e->getMessage() );
485 - }
486 - }
487 -
488 - /**
489 - * Handle params before query courses
490 - *
491 - * @param array $param
492 - * @param LP_Course_Filter $filter
493 - *
494 - * @return void
495 - * @since 4.2.3.3
496 - * @version 1.0.1
497 - * @deprecated 4.2.7.1
498 - */
499 - public static function handle_params_for_query_courses( LP_Course_Filter &$filter, array $param = [] ) {
500 - //_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::handle_params_for_query_courses' );
501 - //return $filter;
502 -
503 - $filter->page = absint( $param['paged'] ?? 1 );
504 - $filter->post_title = LP_Helper::sanitize_params_submitted( trim( $param['c_search'] ?? '' ) );
505 -
506 - // Get Columns
507 - $fields_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_fields'] ?? '' ) );
508 - if ( ! empty( $fields_str ) ) {
509 - $fields = explode( ',', $fields_str );
510 - $filter->fields = $fields;
511 - }
512 -
513 - // Exclude Columns
514 - $fields_exclude_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_exclude_fields'] ?? '' ) );
515 - if ( ! empty( $fields_exclude_str ) ) {
516 - $fields_exclude = explode( ',', $fields_exclude_str );
517 - $filter->exclude_fields = $fields_exclude;
518 - }
519 -
520 - // Author
521 - $filter->post_author = LP_Helper::sanitize_params_submitted( $param['c_author'] ?? 0 );
522 - $author_ids_str = LP_Helper::sanitize_params_submitted( $param['c_authors'] ?? 0 );
523 - if ( ! empty( $author_ids_str ) ) {
524 - $author_ids = explode( ',', $author_ids_str );
525 - $filter->post_authors = $author_ids;
526 - }
527 -
528 - /**
529 - * Sort by
530 - * 1. on_sale
531 - * 2. on_free
532 - * 3. on_paid
533 - * 4. on_feature
534 - */
535 - if ( ! empty( $param['sort_by'] ) ) {
536 - $filter->sort_by[] = $param['sort_by'];
537 - }
538 -
539 - // Sort by level
540 - $levels_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_level'] ?? '' ) );
541 - if ( ! empty( $levels_str ) ) {
542 - $levels_str = str_replace( 'all', '', $levels_str );
543 - $levels = explode( ',', $levels_str );
544 - $filter->levels = $levels;
545 - }
546 -
547 - // Find by category
548 - $term_ids_str = LP_Helper::sanitize_params_submitted( urldecode( $param['term_id'] ?? '' ) );
549 - if ( ! empty( $term_ids_str ) ) {
550 - $term_ids = explode( ',', $term_ids_str );
551 - $filter->term_ids = $term_ids;
552 - }
553 -
554 - // Find by tag
555 - $tag_ids_str = LP_Helper::sanitize_params_submitted( urldecode( $param['tag_id'] ?? '' ) );
556 - if ( ! empty( $tag_ids_str ) ) {
557 - $tag_ids = explode( ',', $tag_ids_str );
558 - $filter->tag_ids = $tag_ids;
559 - }
560 -
561 - // Order by
562 - $filter->order_by = LP_Helper::sanitize_params_submitted( ! empty( $param['order_by'] ) ? $param['order_by'] : 'post_date', 'key' );
563 - $filter->order = LP_Helper::sanitize_params_submitted( ! empty( $param['order'] ) ? $param['order'] : 'DESC' );
564 - $filter->limit = $param['limit'] ?? LP_Settings::get_option( 'archive_course_limit', 10 );
565 -
566 - // For search suggest courses
567 - if ( ! empty( $param['c_suggest'] ) ) {
568 - $filter->only_fields = [ 'ID', 'post_title' ];
569 - $filter->limit = apply_filters( 'learn-press/rest-api/courses/suggest-limit', 10 );
570 - }
571 -
572 - $return_type = $param['return_type'] ?? 'html';
573 - if ( 'json' !== $return_type ) {
574 - $filter->only_fields = array( 'DISTINCT(ID) AS ID' );
575 - }
576 - }
577 -
578 - /**
579 - * Get list course
580 - * Order By: price, title, rating, date ...
581 - * Order: ASC, DES
582 - *
583 - * @param LP_Course_Filter $filter
584 - * @param int $total_rows
585 - *
586 - * @return object|null|string|int
587 - * @author tungnx
588 - * @version 1.0.0
589 - * @sicne 4.1.5
590 - * @deprecated 4.2.7.1
591 - */
592 - public static function get_courses( LP_Course_Filter $filter, int &$total_rows = 0 ) {
593 - //_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::get_courses' );
594 - //return [];
595 -
596 - $lp_course_db = LP_Course_DB::getInstance();
597 -
598 - try {
599 - /*$lp_courses_cache = new LP_Courses_Cache( true );
600 - $key_cache = 'query-courses-' . md5( json_encode( $filter ) );
601 - $key_cache_total_rows = 'query-courses-total-' . md5( json_encode( $filter ) );
602 - $courses_cache = $lp_courses_cache->get_cache( $key_cache );
603 -
604 - if ( false !== $courses_cache ) {
605 - $total_rows = $lp_courses_cache->get_cache( $key_cache_total_rows );
606 - return LP_Helper::json_decode( $courses_cache );
607 - }*/
608 -
609 - // Sort by
610 - $filter->sort_by = (array) $filter->sort_by;
611 - foreach ( $filter->sort_by as $sort_by ) {
612 - $filter_tmp = clone $filter;
613 - $filter_tmp->only_fields = array( 'DISTINCT(ID)' );
614 - $filter_tmp->return_string_query = true;
615 -
616 - switch ( $sort_by ) {
617 - case 'on_sale':
618 - $filter_tmp = $lp_course_db->get_courses_sort_by_sale( $filter_tmp );
619 - break;
620 - case 'on_free':
621 - $filter_tmp = $lp_course_db->get_courses_sort_by_free( $filter_tmp );
622 - break;
623 - case 'on_paid':
624 - $filter_tmp = $lp_course_db->get_courses_sort_by_paid( $filter_tmp );
625 - break;
626 - case 'on_feature':
627 - $filter_tmp = $lp_course_db->get_courses_sort_by_feature( $filter_tmp );
628 - break;
629 - default:
630 - $filter_tmp = apply_filters( 'lp/courses/filter/sort_by/' . $sort_by, $filter_tmp );
631 - break;
632 - }
633 -
634 - $query_courses_str = $lp_course_db->get_courses( $filter_tmp );
635 -
636 - $filter->where[] = "AND ID IN ({$query_courses_str})";
637 - }
638 -
639 - // Order by
640 - switch ( $filter->order_by ) {
641 - case 'price':
642 - case 'price_low':
643 - if ( 'price_low' === $filter->order_by ) {
644 - $filter->order = 'ASC';
645 - } else {
646 - $filter->order = 'DESC';
647 - }
648 -
649 - $filter = $lp_course_db->get_courses_order_by_price( $filter );
650 - break;
651 - case 'popular':
652 - $filter = $lp_course_db->get_courses_order_by_popular( $filter );
653 - break;
654 - case 'post_title':
655 - $filter->order = 'ASC';
656 - break;
657 - case 'post_title_desc':
658 - $filter->order_by = 'post_title';
659 - $filter->order = 'DESC';
660 - break;
661 - case 'menu_order':
662 - $filter->order_by = 'menu_order';
663 - $filter->order = 'ASC';
664 - break;
665 - default:
666 - $filter = apply_filters( 'lp/courses/filter/order_by/' . $filter->order_by, $filter );
667 - break;
668 - }
669 -
670 - // Query get results
671 - $filter = apply_filters( 'lp/courses/filter', $filter );
672 - $courses = LP_Course_DB::getInstance()->get_courses( $filter, $total_rows );
673 -
674 - //$lp_courses_cache->set_cache( $key_cache, json_encode( $courses ) );
675 - //$lp_courses_cache->set_cache( $key_cache_total_rows, $total_rows );
676 - } catch ( Throwable $e ) {
677 - $courses = [];
678 - error_log( __FUNCTION__ . ': ' . $e->getMessage() );
679 - }
680 -
681 - return $courses;
682 - }
683 -
684 - /**
685 - * Get full sections, items of course via Cache, extra info (if it has)
686 - *
687 - * @return array
688 - * @since 4.1.6.9
689 - * @version 1.0.1
690 - * @author tungnx
691 - */
692 - public function get_full_sections_and_items_course() {
693 - $sections_items = [];
694 - $course_id = $this->get_id();
695 -
696 - try {
697 - $courseModel = CourseModel::find( $course_id, true );
698 - return $courseModel->get_section_items();
699 -
700 - // Get cache
701 - $lp_course_cache = LP_Course_Cache::instance();
702 - $key_cache = "$course_id/sections_items";
703 - $sections_items = $lp_course_cache->get_cache( $key_cache );
704 -
705 - if ( ! $sections_items ) {
706 - $extra_info = $this->get_info_extra_for_fast_query();
707 -
708 - if ( empty( $extra_info->sections_items ) ) {
709 - $sections_items = $this->get_sections_and_items_course_from_db_and_sort();
710 - $extra_info->sections_items = $sections_items;
711 -
712 - // Save post meta
713 - $this->set_info_extra_for_fast_query( $extra_info );
714 - } else {
715 - $sections_items = $extra_info->sections_items;
716 - }
717 -
718 - $lp_course_cache->set_cache( $key_cache, $sections_items );
719 - }
720 - } catch ( Throwable $e ) {
721 - if ( LP_Debug::is_debug() ) {
722 - error_log( $e->getMessage() );
723 - }
724 - }
725 -
726 - return $sections_items;
727 - }
728 -
729 - /**
730 - * Get all sections and items from database, then handle sort
731 - * Only call when data change or not set
732 - *
733 - * @return array
734 - * @since 4.1.6.9
735 - * @version 1.0.0
736 - * @author tungnx
737 - */
738 - public function get_sections_and_items_course_from_db_and_sort(): array {
739 - $sections_items = [];
740 - $course_id = $this->get_id();
741 - $lp_course_db = LP_Course_DB::getInstance();
742 - $lp_course_cache = LP_Course_Cache::instance();
743 - $key_cache = "$course_id/sections_items";
744 -
745 - try {
746 - $sections_results = $lp_course_db->get_sections( $course_id );
747 - $sections_items_results = $lp_course_db->get_full_sections_and_items_course( $course_id );
748 - $count_items = count( $sections_items_results );
749 - $index_items_last = $count_items - 1;
750 - $section_current = 0;
751 -
752 - foreach ( $sections_items_results as $index => $sections_item ) {
753 - $section_new = $sections_item->section_id;
754 - $section_order = $sections_item->section_order;
755 - $item = new stdClass();
756 - $item->id = $sections_item->item_id;
757 - $item->order = $sections_item->item_order;
758 - $item->type = $sections_item->item_type;
759 -
760 - if ( $section_new != $section_current ) {
761 - $sections_items[ $section_new ] = new stdClass();
762 - $sections_items[ $section_new ]->id = $section_new; // old field will be deprecated in future
763 - $sections_items[ $section_new ]->section_id = $section_new; // new field
764 - $sections_items[ $section_new ]->order = $section_order; // old field will be deprecated in future
765 - $sections_items[ $section_new ]->section_order = $section_order; // new field
766 - $sections_items[ $section_new ]->title = html_entity_decode( $sections_item->section_name ); // old field will be deprecated in future
767 - $sections_items[ $section_new ]->section_name = html_entity_decode( $sections_item->section_name ); // new field
768 - $sections_items[ $section_new ]->description = html_entity_decode( $sections_item->section_description ); // old field will be deprecated in future
769 - $sections_items[ $section_new ]->section_description = html_entity_decode( $sections_item->section_description ); // new field
770 - $sections_items[ $section_new ]->items = [];
771 -
772 - // Sort item by item_order
773 - if ( $section_current != 0 ) {
774 - usort(
775 - $sections_items[ $section_current ]->items,
776 - function ( $item1, $item2 ) {
777 - return $item1->order - $item2->order;
778 - }
779 - );
780 - }
781 -
782 - $section_current = $section_new;
783 - }
784 -
785 - $sections_items[ $section_new ]->items[ $item->id ] = $item;
786 -
787 - if ( $index_items_last === $index ) {
788 - usort(
789 - $sections_items[ $section_current ]->items,
790 - function ( $item1, $item2 ) {
791 - return $item1->order - $item2->order;
792 - }
793 - );
794 - }
795 - }
796 -
797 - // Check case if section empty items
798 - foreach ( $sections_results as $section ) {
799 - $section_id = $section->section_id;
800 - if ( isset( $sections_items[ $section_id ] ) ) {
801 - continue;
802 - }
803 -
804 - $section_obj = new stdClass();
805 - $section_obj->id = $section_id;
806 - $section_obj->order = $section->section_order;
807 - $section_obj->title = html_entity_decode( $section->section_name );
808 - $section_obj->description = html_entity_decode( $section->section_description );
809 - $section_obj->items = [];
810 - $sections_items[ $section_id ] = $section_obj;
811 - }
812 -
813 - // Sort section by section_order
814 - usort(
815 - $sections_items,
816 - function ( $section1, $section2 ) {
817 - return $section1->order - $section2->order;
818 - }
819 - );
820 -
821 - $lp_course_cache->set_cache( $key_cache, $sections_items );
822 - } catch ( Throwable $e ) {
823 - if ( LP_Debug::is_debug() ) {
824 - error_log( $e->getMessage() );
825 - }
826 - }
827 -
828 - return $sections_items;
829 - }
830 -
831 - /**
832 - * Get sections of course.
833 - *
834 - * @param string $return.
835 - * @param int $section_id.
836 - *
837 - * @return array|bool|LP_Course_Section[]|LP_Course_Section
838 - * @version 4.0.0
839 - */
840 - public function get_sections( $return = 'object', $section_id = 0 ) {
841 - // $this->load_curriculum();
842 - // $sections = LP_Course_Utils::get_cached_db_sections( $this->get_id() );
843 -
844 - $sections_items = $this->get_full_sections_and_items_course();
845 -
846 - /*if ( false === $sections ) {
847 - return false;
848 - }*/
849 -
850 - //$position = 0;
851 - $sections = array();
852 - foreach ( $sections_items as $k => $section_items ) {
853 - $position = $k + 1;
854 - $section_items_tmp = [
855 - 'section_id' => $section_items->id,
856 - 'section_name' => $section_items->title,
857 - 'section_course_id' => $this->get_id(),
858 - 'section_order' => $section_items->order,
859 - 'section_description' => $section_items->description,
860 - 'items' => $section_items->items,
861 - ];
862 - $sid = $section_items->id;
863 - $section = new LP_Course_Section( $section_items_tmp );
864 - $section->set_position( $position );
865 - $sections[ $sid ] = $section;
866 - }
867 -
868 - if ( $section_id ) {
869 - $sections = $sections[ $section_id ] ?? [];
870 - }
871 -
872 - return apply_filters( 'learn-press/course-sections', $sections, $this->get_id(), $return, $section_id );
873 - }
874 -
875 - /**
876 - * Get sections arr
877 - *
878 - * @param int $section_id
879 - *
880 - * @return array
881 - * @version 1.0.0
882 - * @sicne 4.1.7
883 - */
884 - public function get_sections_data_arr( int $section_id = 0 ): array {
885 - $sections_items = $this->get_full_sections_and_items_course();
886 - $sections = array();
887 -
888 - foreach ( $sections_items as $section_items ) {
889 - $section = [
890 - 'section_id' => $section_items->id,
891 - 'section_name' => $section_items->title,
892 - 'section_course_id' => $this->get_id(),
893 - 'section_order' => $section_items->order,
894 - 'section_description' => $section_items->description,
895 - 'items' => $section_items->items,
896 - ];
897 -
898 - if ( $section_id && $section_id == $section['section_id'] ) {
899 - $sections = $section;
900 - break;
901 - }
902 -
903 - $sections[] = $section;
904 - }
905 -
906 - return $sections;
907 - }
908 -
909 - /**
910 - * Get raw data curriculum.
911 - *
912 - * @return array
913 - * @since 3.0.0
914 - * @editor tungnx
915 - * @version 1.0.1
916 - */
917 - public function get_curriculum_raw(): array {
918 - $sections_data = array();
919 -
920 - $sections_items = $this->get_full_sections_and_items_course();
921 - foreach ( $sections_items as $section_items ) {
922 - $section_items_arr = (array) $section_items;
923 - $section_items_arr['course_id'] = $this->get_id();
924 - $section_items_arr['items'] = [];
925 -
926 - foreach ( $section_items->items as $item ) {
927 - $itemObject = $this->get_item( $item->id );
928 -
929 - if ( ! $itemObject instanceof LP_Course_Item ) {
930 - continue;
931 - }
932 -
933 - $item_arr = (array) $item;
934 - $item_arr['title'] = html_entity_decode( $itemObject->get_title() );
935 - $item_arr['preview'] = $itemObject->is_preview();
936 - $section_items_arr['items'][] = apply_filters( 'learn-press/item/to_array', $item_arr );
937 - }
938 -
939 - $sections_data[] = $section_items_arr;
940 - }
941 -
942 - return $sections_data;
943 - }
944 -
945 - /**
946 - * Get all curriculum of this course.
947 - *
948 - * @param int $section_id
949 - * @param bool $force
950 - *
951 - * @return bool|LP_Course_Section[]
952 - */
953 - public function get_curriculum( $section_id = 0, $force = false ) {
954 - return $this->get_sections( 'object', $section_id );
955 - }
956 -
957 - /**
958 - * Return list of item's ids in course's curriculum.
959 - *
960 - * @param string|array $type
961 - *
962 - * @return array
963 - * @since 3.0.0
964 - * @version 3.0.2
965 - */
966 - public function get_items( $type = '' ) {
967 - //$this->load();
968 -
969 - $sections_items = $this->get_full_sections_and_items_course();
970 -
971 - $items = array();
972 -
973 - foreach ( $sections_items as $section_items ) {
974 - foreach ( $section_items->items as $item ) {
975 - if ( ! empty( $type ) ) {
976 - if ( $type === $item->type ) {
977 - $items[] = $item->id;
978 - }
979 - } else {
980 - $items[] = $item->id;
981 - }
982 - }
983 - }
984 -
985 - return $items;
986 - }
987 -
988 - /**
989 - * Get all items in a course.
990 - *
991 - * @param string $type . Type of items, eg: lp_lesson, lp_quiz...
992 - *
993 - * @return array
994 - * @deprecated
995 - */
996 - public function get_curriculum_items( $type = '' ) {
997 - return $this->get_items( $type );
998 - }
999 -
1000 - /**
1001 - * Get evaluation type
1002 - *
1003 - * @since 4.2.1
1004 - * @version 1.0.0
1005 - * @return string
1006 - */
1007 - public function get_evaluation_type(): string {
1008 - $evaluation_type = get_post_meta( $this->get_id(), '_lp_course_result', true );
1009 - if ( ! $evaluation_type ) {
1010 - $evaluation_type = 'evaluate_lesson';
1011 - }
1012 -
1013 - return $evaluation_type;
1014 - }
1015 -
1016 - /**
1017 - * Get categories of course.
1018 - *
1019 - * @since 4.2.3
1020 - * @version 1.0.0
1021 - * @return array|WP_Term[]
1022 - */
1023 - public function get_categories(): array {
1024 - // Todo: set cache.
1025 - $categories = get_the_terms( $this->get_id(), LP_COURSE_CATEGORY_TAX );
1026 - if ( ! $categories ) {
1027 - $categories = array();
1028 - }
1029 -
1030 - return $categories;
1031 - }
1032 -
1033 - /**
1034 - * Get tags of course.
1035 - *
1036 - * @since 4.2.3
1037 - * @version 1.0.0
1038 - * @return array|WP_Term[]
1039 - */
1040 - public function get_tags(): array {
1041 - // Todo: set cache.
1042 - $tags = get_the_terms( $this->get_id(), LP_COURSE_TAXONOMY_TAG );
1043 - if ( ! $tags ) {
1044 - $tags = array();
1045 - }
1046 -
1047 - return $tags;
1048 - }
1049 -
1050 - /**
1051 - * Get all categories.
1052 - *
1053 - * @return array
1054 - */
1055 - public static function get_all_categories(): array {
1056 - // Todo: set cache.
1057 - $categories = get_terms( LP_COURSE_CATEGORY_TAX );
1058 - if ( ! $categories ) {
1059 - $categories = array();
1060 - }
1061 -
1062 - return $categories;
1063 - }
1064 -
1065 - /**
1066 - * Get all curriculum of this course.
1067 - *
1068 - * @return bool|LP_Course_Section
1069 - * @since 4.2.3.5
1070 - */
1071 - public function get_level(): string {
1072 - $level = get_post_meta( $this->get_id(), '_lp_level', true );
1073 - if ( ! $level ) {
1074 - $level = '';
1075 - }
1076 -
1077 - return $level;
1078 - }
1079 -
1080 - /**
1081 - * Get duration of this course.
1082 - *
1083 - * @return bool|LP_Course_Section
1084 - * @since 4.2.3.5
1085 - */
1086 - public function get_duration(): string {
1087 - $duration = get_post_meta( $this->get_id(), '_lp_duration', true );
1088 -
1089 - return $duration;
1090 - }
1091 -
1092 - /**
1093 - * Check if a course is enabled Offline
1094 - *
1095 - * @return bool
1096 - */
1097 - public function is_offline(): bool {
1098 - return get_post_meta( $this->get_id(), CoursePostModel::META_KEY_OFFLINE_COURSE, 'no' ) === 'yes';
1099 - }
1100 - }
1101 -}
1 +<?php
2 +/**
3 + * Class LP_Course.
4 + *
5 + * @author ThimPress
6 + * @package LearnPress/Classes
7 + * @version 4.0.1
8 + */
9 +
10 +use LearnPress\Models\CourseModel;
11 +use LearnPress\Models\CoursePostModel;
12 +
13 +defined( 'ABSPATH' ) || exit();
14 +
15 +if ( ! class_exists( 'LP_Course' ) ) {
16 +
17 + /**
18 + * Class LP_Course
19 + */
20 + class LP_Course extends LP_Abstract_Course {
21 + protected $key_info_extra_fast_query = '_lp_info_extra_fast_query';
22 +
23 + /**
24 + * LP_Course constructor.
25 + *
26 + * @param mixed $course
27 + */
28 + public function __construct( $course ) {
29 + parent::__construct( $course );
30 + }
31 +
32 + /**
33 + * Debug log.
34 + *
35 + * @param $data
36 + *
37 + * @return array
38 + * @editor tungnx
39 + * @modify 4.1.5 - comment - not use
40 + */
41 + /*public static function log( $data ) {
42 + return $data;
43 + }*/
44 +
45 + /**
46 + * Get default course meta.
47 + *
48 + * @return mixed
49 + * @since 3.0.0
50 + */
51 + public static function get_default_meta() {
52 + $meta = array(
53 + 'duration' => '10 weeks',
54 + 'max_students' => 1000,
55 + 'students' => 0,
56 + 'retake_count' => 0,
57 + 'featured' => 'no',
58 + //'block_lesson_content' => 'no',
59 + 'external_link_buy_course' => '',
60 + 'course_result' => 'evaluate_lesson',
61 + 'passing_condition' => 80,
62 + 'price' => '',
63 + 'sale_price' => '',
64 + 'sale_start' => '',
65 + 'sale_end' => '',
66 + 'required_enroll' => 'yes',
67 + 'course_author' => learn_press_get_current_user_id(),
68 + );
69 +
70 + return apply_filters( 'learn-press/course/default-meta', $meta );
71 + }
72 +
73 + /**
74 + * Get LP Course.
75 + *
76 + * @param int $course_id
77 + *
78 + * @return mixed|bool|LP_Course
79 + * @Todo - Tungnx review to rewrite this method.
80 + */
81 + public static function get_course( int $course_id = 0 ) {
82 + if ( isset( LP_Global::$courses[ $course_id ] ) ) {
83 + return LP_Global::$courses[ $course_id ];
84 + }
85 +
86 + $the_course = self::get_course_object( $course_id );
87 +
88 + if ( ! $the_course ) {
89 + return false;
90 + }
91 +
92 + $key_args = wp_parse_args(
93 + array(
94 + 'id' => $the_course->ID,
95 + 'type' => $the_course->post_type,
96 + )
97 + );
98 +
99 + $key = LP_Helper::array_to_md5( $key_args );
100 +
101 + if ( empty( LP_Global::$courses[ $key ] ) ) {
102 + $class_name = self::get_course_class( $the_course );
103 + if ( is_string( $class_name ) && class_exists( $class_name ) ) {
104 + $course = new $class_name( $the_course->ID );
105 + } elseif ( $class_name instanceof LP_Abstract_Course ) {
106 + $course = $class_name;
107 + } else {
108 + $course = new self( $the_course->ID );
109 + }
110 + LP_Global::$courses[ $key ] = $course;
111 + } else {
112 + $course = LP_Global::$courses[ $key ];
113 + }
114 +
115 + /**
116 + * Force to reload course data into cache if it is not
117 + * loaded or has been deleted for some reasons.
118 + */
119 + $course->load();
120 +
121 + return $course;
122 + }
123 +
124 + /**
125 + * @param string $course_type
126 + *
127 + * @return string|false
128 + */
129 + private static function get_class_name_from_course_type( $course_type ) {
130 + return LP_COURSE_CPT === $course_type ? __CLASS__ : 'LP_Course_' . implode(
131 + '_',
132 + array_map( 'ucfirst', explode( '-', $course_type ) )
133 + );
134 + }
135 +
136 + /**
137 + * Get the course class name
138 + *
139 + * @param WP_Post $the_course
140 + * @param array $args (default: array())
141 + *
142 + * @return string
143 + */
144 + private static function get_course_class( $the_course, $args = array() ) {
145 + $course_id = absint( $the_course->ID );
146 + $type = $the_course->post_type;
147 +
148 + $class_name = self::get_class_name_from_course_type( $type );
149 +
150 + // Filter class name so that the class can be overridden if extended.
151 + return apply_filters( 'learn-press/course/object-class', $class_name, $type, $course_id );
152 + }
153 +
154 + /**
155 + * Get the course object
156 + *
157 + * @param mixed $the_course
158 + *
159 + * @return WP_Post|bool false on failure
160 + * @uses WP_Post
161 + */
162 + private static function get_course_object( $the_course ) {
163 + $the_course_passed = $the_course;
164 + if ( false === $the_course ) {
165 + $the_course = get_post_type() === LP_COURSE_CPT ? $GLOBALS['post'] : false;
166 + } elseif ( is_numeric( $the_course ) ) {
167 + $the_course = get_post( $the_course );
168 + } elseif ( $the_course instanceof LP_Abstract_Course ) {
169 + $the_course = get_post( $the_course->get_id() );
170 + } elseif ( ! ( $the_course instanceof WP_Post ) ) {
171 + $the_course = false;
172 + }
173 +
174 + if ( $the_course && $the_course->post_type !== LP_COURSE_CPT ) {
175 + $the_course = false;
176 + }
177 +
178 + return apply_filters( 'learn-press/course/post-object', $the_course, $the_course_passed );
179 + }
180 +
181 + /**
182 + * Check time remaining course when enable duration expire
183 + * Value: -1 is no limit (default)
184 + * Value: 0 is block
185 + * Administrator || (is instructor && is author course) will be not block.
186 + *
187 + * @return int second
188 + * @since 4.0.0
189 + * @author tungnx
190 + * @version 1.0.1
191 + */
192 + public function timestamp_remaining_duration(): int {
193 + $timestamp_remaining = - 1;
194 + $user = learn_press_get_user( get_current_user_id() );
195 +
196 + if ( current_user_can( 'administrator' ) ||
197 + ( current_user_can( LP_TEACHER_ROLE ) && $this->get_author()->get_id() === $user->get_id() ) ) {
198 + return $timestamp_remaining;
199 + }
200 +
201 + if ( 0 === absint( $this->get_data( 'duration' ) ) ) {
202 + return $timestamp_remaining;
203 + }
204 +
205 + if ( 'yes' !== $this->get_data( 'block_course_duration_expire' ) ) {
206 + return $timestamp_remaining;
207 + }
208 +
209 + if ( $user instanceof LP_User_Guest ) {
210 + return $timestamp_remaining;
211 + }
212 +
213 + $course_item_data = $user->get_course_data( $this->get_id() );
214 +
215 + if ( ! $course_item_data ) {
216 + return $timestamp_remaining;
217 + }
218 +
219 + $course_start_time = $course_item_data->get_start_time()->get_raw_date();
220 + $duration = $this->get_data( 'duration' );
221 + $timestamp_expire = strtotime( $course_start_time . ' +' . $duration );
222 + $timestamp_current = time();
223 + $timestamp_remaining = $timestamp_expire - $timestamp_current;
224 +
225 + if ( $timestamp_remaining < 0 ) {
226 + $timestamp_remaining = 0;
227 + }
228 +
229 + return apply_filters( 'learnpress/course/block_duration_expire/timestamp_remaining', $timestamp_remaining );
230 + }
231 +
232 + /**
233 + * Get option enable block course when finish course
234 + *
235 + * @return bool
236 + */
237 + public function enable_block_item_when_finish(): bool {
238 + return 'yes' === $this->get_data( 'block_course_finished' );
239 + }
240 +
241 + public function allow_repurchase(): bool {
242 + return 'yes' === $this->get_data( 'allow_repurchase' );
243 + }
244 +
245 + public function allow_repurchase_course_option(): string {
246 + return $this->get_data( 'allow_repurchase_course_option', 'reset' );
247 + }
248 +
249 + /**
250 + * Get first item of course
251 + *
252 + * @author tungnx
253 + * @since 4.0.0
254 + * @modify 4.1.3
255 + * @version 1.0.2
256 + * @return int
257 + */
258 + public function get_first_item_id(): int {
259 + $course_id = $this->get_id();
260 +
261 + $courseModel = CourseModel::find( $course_id, true );
262 + return $courseModel->get_first_item_id();
263 +
264 + try {
265 + // Get cache
266 + $lp_course_cache = LP_Course_Cache::instance();
267 + $key_cache = "$course_id/first_item_id";
268 + $first_item_id = $lp_course_cache->get_cache( $key_cache );
269 +
270 + if ( ! $first_item_id ) {
271 + $extra_info = $this->get_info_extra_for_fast_query();
272 +
273 + if ( ! $extra_info->first_item_id ) {
274 + $first_item_id = LP_Course_DB::getInstance()->get_first_item_id( $course_id );
275 + $extra_info->first_item_id = $first_item_id;
276 +
277 + // Save post meta
278 + $this->set_info_extra_for_fast_query( $extra_info );
279 + } else {
280 + $first_item_id = $extra_info->first_item_id;
281 + }
282 +
283 + $lp_course_cache->set_cache( $key_cache, $first_item_id );
284 + }
285 + } catch ( Throwable $e ) {
286 + $first_item_id = 0;
287 + }
288 +
289 + return $first_item_id;
290 + }
291 +
292 + /**
293 + * Get redirect url after enroll course
294 + *
295 + * @author tungnx
296 + * @version 1.0.0
297 + * @since 4.0.0
298 + * @return false|string|WP_Error
299 + */
300 + public function get_redirect_url_after_enroll() {
301 + $first_item_id = $this->get_first_item_id();
302 + $redirect = $first_item_id ? $this->get_item_link( $first_item_id ) : get_the_permalink( $this->get_id() );
303 +
304 + return apply_filters( 'learnpress/rest-api/enroll-course/redirect', $redirect );
305 + }
306 +
307 + /**
308 + * Get info extra on post meta to query fast
309 + *
310 + * @since 4.1.3
311 + * @author tungnx
312 + * @version 1.0.0
313 + * @return LP_Course_Extra_Info_Fast_Query_Model
314 + * @deprecated 4.2.7.4
315 + */
316 + public function get_info_extra_for_fast_query(): LP_Course_Extra_Info_Fast_Query_Model {
317 + $extra_info = new LP_Course_Extra_Info_Fast_Query_Model();
318 +
319 + try {
320 + $extra_info_str = get_post_meta( $this->get_id(), $this->key_info_extra_fast_query, true );
321 +
322 + if ( ! empty( $extra_info_str ) ) {
323 + $extra_info_stdclass = LP_Helper::json_decode( $extra_info_str );
324 + $extra_info = $extra_info->map_stdclass( $extra_info_stdclass );
325 + }
326 + } catch ( Throwable $e ) {
327 + error_log( __METHOD__ . ': ' . $e->getMessage() );
328 + }
329 +
330 + return $extra_info;
331 + }
332 +
333 + /**
334 + * Set extra info for query fast on post meta
335 + *
336 + * @since 4.1.3
337 + * @author tungnx
338 + * @version 1.0.0
339 + * @param LP_Course_Extra_Info_Fast_Query_Model $data_object
340 + */
341 + public function set_info_extra_for_fast_query( LP_Course_Extra_Info_Fast_Query_Model $data_object ) {
342 + try {
343 + $extra_info_json = json_encode( $data_object, JSON_UNESCAPED_UNICODE );
344 +
345 + if ( JSON_ERROR_NONE !== json_last_error() ) {
346 + throw new Exception( 'Error encode on ' . __METHOD__ );
347 + }
348 +
349 + update_post_meta( $this->get_id(), $this->key_info_extra_fast_query, $extra_info_json );
350 + } catch ( Throwable $e ) {
351 + error_log( $e->getMessage() );
352 + }
353 + }
354 +
355 + /**
356 + * Get info total items of Course
357 + *
358 + * @param string $type
359 + * @param bool $include_preview
360 + *
361 + * @return int
362 + * @throws Exception
363 + * @version 1.0.1
364 + * @author tungnx
365 + * @since 4.1.4.1
366 + */
367 + public function count_items( string $type = '', bool $include_preview = true ): int {
368 + $course_id = $this->get_id();
369 + $courseModel = CourseModel::find( $course_id, true );
370 + if ( ! $courseModel ) {
371 + return 0;
372 + }
373 +
374 + $total_items = $courseModel->get_total_items();
375 + if ( empty( $total_items ) ) {
376 + return 0;
377 + }
378 +
379 + if ( ! empty( $type ) ) {
380 + if ( isset( $total_items->{$type} ) ) {
381 + return $total_items->{$type};
382 + }
383 + } else {
384 + return $total_items->count_items ?? 0;
385 + }
386 +
387 + return 0;
388 + }
389 +
390 + /**
391 + * Delete relate data when delete course
392 + *
393 + * @since 4.1.4.1
394 + * @author tungnx
395 + * @version 1.0.0
396 + */
397 + public function delete_relate_data_when_delete_course() {
398 + $lp_section_db = LP_Section_DB::getInstance();
399 + $lp_user_items_db = LP_User_Items_DB::getInstance();
400 +
401 + try {
402 + // Check valid user
403 + if ( ! current_user_can( 'administrator' ) && $this->get_author( 'id' ) !== get_current_user_id() ) {
404 + return;
405 + }
406 +
407 + $section_ids = $lp_section_db->get_section_ids_by_course( $this->get_id() );
408 +
409 + $filter = new LP_Section_Filter();
410 + $filter->section_ids = $section_ids;
411 + $filter->author_id_course = $this->get_author( 'id' );
412 +
413 + // Delete section
414 + $lp_section_db->delete_section( $filter );
415 + // Delete section items
416 + $lp_section_db->delete_section_items( $filter );
417 +
418 + $filter_user_items = new LP_User_Items_Filter();
419 + $filter_user_items->item_id = $this->get_id();
420 + $user_course_ids = $lp_user_items_db->get_user_items_by_course( $filter_user_items );
421 +
422 + $this->delete_user_item_and_result( $user_course_ids );
423 +
424 + // Clear cache total students enrolled.
425 + $lp_course_cache = new LP_Course_Cache( true );
426 + $lp_course_cache->clean_total_students_enrolled( $this->get_id() );
427 + $lp_course_cache->clean_total_students_enrolled_or_purchased( $this->get_id() );
428 + // Clear cache count students many courses.
429 + $lp_courses_cache = new LP_Courses_Cache( true );
430 + $lp_courses_cache->clear_cache_on_group( LP_Courses_Cache::KEYS_COUNT_STUDENT_COURSES );
431 + $lp_course_cache->clear_cache_on_group( LP_Courses_Cache::KEYS_COUNT_COURSES_FREE );
432 + // Clear cache user course.
433 + $lp_user_items_cache = new LP_User_Items_Cache();
434 + $lp_user_items_cache->clean_user_items_by_course( $this->get_id() );
435 + } catch ( Throwable $e ) {
436 + error_log( __FUNCTION__ . ':' . $e->getMessage() );
437 + }
438 + }
439 +
440 + /**
441 + * Delete user_items, user_itemmeta, user_item_results
442 + * WHERE IN user_item_ids
443 + *
444 + * @param array $user_course_ids
445 + */
446 + public function delete_user_item_and_result( array $user_course_ids ) {
447 + $lp_user_items_db = LP_User_Items_DB::getInstance();
448 + $lp_user_item_results = LP_User_Items_Result_DB::instance();
449 +
450 + try {
451 + if ( empty( $user_course_ids ) ) {
452 + return;
453 + }
454 +
455 + // Get user_item_ids has parent in $user_course_ids
456 + $filter = new LP_User_Items_Filter();
457 + $filter->user_item_ids = $user_course_ids;
458 + $user_item_ids = $lp_user_items_db->get_item_ids_of_user_course( $filter );
459 +
460 + $user_item_ids_concat = array_merge( $user_course_ids, $user_item_ids );
461 +
462 + // Delete on tb lp_user_items
463 + $filter_delete = new LP_User_Items_Filter();
464 + $filter_delete->user_item_ids = $user_item_ids_concat;
465 + $user_items_rs = $lp_user_items_db->get_user_items( $filter_delete );
466 +
467 + // For each to clear cache
468 + foreach ( $user_items_rs as $user_item ) {
469 + $lp_user_items_cache = new LP_User_Items_Cache();
470 + $key_one = "userItemModel/find/{$user_item->user_id}/{$user_item->item_id}/{$user_item->item_type}";
471 + $key_two = "userItemModel/find/{$user_item->user_id}/{$user_item->item_id}/{$user_item->item_type}/{$user_item->ref_id}/{$user_item->ref_type}";
472 + $lp_user_items_cache->clear( $key_one );
473 + $lp_user_items_cache->clear( $key_two );
474 + }
475 +
476 + $lp_user_items_db->remove_user_item_ids( $filter_delete );
477 +
478 + // Delete user_itemmeta
479 + $lp_user_items_db->remove_user_itemmeta( $filter_delete );
480 +
481 + // Delete user_item_results
482 + $lp_user_item_results->remove_user_item_results( $filter_delete );
483 + } catch ( Throwable $e ) {
484 + error_log( __FUNCTION__ . ':' . $e->getMessage() );
485 + }
486 + }
487 +
488 + /**
489 + * Handle params before query courses
490 + *
491 + * @param array $param
492 + * @param LP_Course_Filter $filter
493 + *
494 + * @return void
495 + * @since 4.2.3.3
496 + * @version 1.0.1
497 + * @deprecated 4.2.7.1
498 + */
499 + public static function handle_params_for_query_courses( LP_Course_Filter &$filter, array $param = [] ) {
500 + //_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::handle_params_for_query_courses' );
501 + //return $filter;
502 +
503 + $filter->page = absint( $param['paged'] ?? 1 );
504 + $filter->post_title = LP_Helper::sanitize_params_submitted( trim( $param['c_search'] ?? '' ) );
505 +
506 + // Get Columns
507 + $fields_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_fields'] ?? '' ) );
508 + if ( ! empty( $fields_str ) ) {
509 + $fields = explode( ',', $fields_str );
510 + $filter->fields = $fields;
511 + }
512 +
513 + // Exclude Columns
514 + $fields_exclude_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_exclude_fields'] ?? '' ) );
515 + if ( ! empty( $fields_exclude_str ) ) {
516 + $fields_exclude = explode( ',', $fields_exclude_str );
517 + $filter->exclude_fields = $fields_exclude;
518 + }
519 +
520 + // Author
521 + $filter->post_author = LP_Helper::sanitize_params_submitted( $param['c_author'] ?? 0 );
522 + $author_ids_str = LP_Helper::sanitize_params_submitted( $param['c_authors'] ?? 0 );
523 + if ( ! empty( $author_ids_str ) ) {
524 + $author_ids = explode( ',', $author_ids_str );
525 + $filter->post_authors = $author_ids;
526 + }
527 +
528 + /**
529 + * Sort by
530 + * 1. on_sale
531 + * 2. on_free
532 + * 3. on_paid
533 + * 4. on_feature
534 + */
535 + if ( ! empty( $param['sort_by'] ) ) {
536 + $filter->sort_by[] = $param['sort_by'];
537 + }
538 +
539 + // Sort by level
540 + $levels_str = LP_Helper::sanitize_params_submitted( urldecode( $param['c_level'] ?? '' ) );
541 + if ( ! empty( $levels_str ) ) {
542 + $levels_str = str_replace( 'all', '', $levels_str );
543 + $levels = explode( ',', $levels_str );
544 + $filter->levels = $levels;
545 + }
546 +
547 + // Find by category
548 + $term_ids_str = LP_Helper::sanitize_params_submitted( urldecode( $param['term_id'] ?? '' ) );
549 + if ( ! empty( $term_ids_str ) ) {
550 + $term_ids = explode( ',', $term_ids_str );
551 + $filter->term_ids = $term_ids;
552 + }
553 +
554 + // Find by tag
555 + $tag_ids_str = LP_Helper::sanitize_params_submitted( urldecode( $param['tag_id'] ?? '' ) );
556 + if ( ! empty( $tag_ids_str ) ) {
557 + $tag_ids = explode( ',', $tag_ids_str );
558 + $filter->tag_ids = $tag_ids;
559 + }
560 +
561 + // Order by
562 + $filter->order_by = LP_Helper::sanitize_params_submitted( ! empty( $param['order_by'] ) ? $param['order_by'] : 'post_date', 'key' );
563 + $filter->order = LP_Helper::sanitize_params_submitted( ! empty( $param['order'] ) ? $param['order'] : 'DESC' );
564 + $filter->limit = $param['limit'] ?? LP_Settings::get_option( 'archive_course_limit', 10 );
565 +
566 + // For search suggest courses
567 + if ( ! empty( $param['c_suggest'] ) ) {
568 + $filter->only_fields = [ 'ID', 'post_title' ];
569 + $filter->limit = apply_filters( 'learn-press/rest-api/courses/suggest-limit', 10 );
570 + }
571 +
572 + $return_type = $param['return_type'] ?? 'html';
573 + if ( 'json' !== $return_type ) {
574 + $filter->only_fields = array( 'DISTINCT(ID) AS ID' );
575 + }
576 + }
577 +
578 + /**
579 + * Get list course
580 + * Order By: price, title, rating, date ...
581 + * Order: ASC, DES
582 + *
583 + * @param LP_Course_Filter $filter
584 + * @param int $total_rows
585 + *
586 + * @return object|null|string|int
587 + * @author tungnx
588 + * @version 1.0.0
589 + * @sicne 4.1.5
590 + * @deprecated 4.2.7.1
591 + */
592 + public static function get_courses( LP_Course_Filter $filter, int &$total_rows = 0 ) {
593 + //_deprecated_function( __METHOD__, '4.2.7.1', 'Courses::get_courses' );
594 + //return [];
595 +
596 + $lp_course_db = LP_Course_DB::getInstance();
597 +
598 + try {
599 + /*$lp_courses_cache = new LP_Courses_Cache( true );
600 + $key_cache = 'query-courses-' . md5( json_encode( $filter ) );
601 + $key_cache_total_rows = 'query-courses-total-' . md5( json_encode( $filter ) );
602 + $courses_cache = $lp_courses_cache->get_cache( $key_cache );
603 +
604 + if ( false !== $courses_cache ) {
605 + $total_rows = $lp_courses_cache->get_cache( $key_cache_total_rows );
606 + return LP_Helper::json_decode( $courses_cache );
607 + }*/
608 +
609 + // Sort by
610 + $filter->sort_by = (array) $filter->sort_by;
611 + foreach ( $filter->sort_by as $sort_by ) {
612 + $filter_tmp = clone $filter;
613 + $filter_tmp->only_fields = array( 'DISTINCT(ID)' );
614 + $filter_tmp->return_string_query = true;
615 +
616 + switch ( $sort_by ) {
617 + case 'on_sale':
618 + $filter_tmp = $lp_course_db->get_courses_sort_by_sale( $filter_tmp );
619 + break;
620 + case 'on_free':
621 + $filter_tmp = $lp_course_db->get_courses_sort_by_free( $filter_tmp );
622 + break;
623 + case 'on_paid':
624 + $filter_tmp = $lp_course_db->get_courses_sort_by_paid( $filter_tmp );
625 + break;
626 + case 'on_feature':
627 + $filter_tmp = $lp_course_db->get_courses_sort_by_feature( $filter_tmp );
628 + break;
629 + default:
630 + $filter_tmp = apply_filters( 'lp/courses/filter/sort_by/' . $sort_by, $filter_tmp );
631 + break;
632 + }
633 +
634 + $query_courses_str = $lp_course_db->get_courses( $filter_tmp );
635 +
636 + $filter->where[] = "AND ID IN ({$query_courses_str})";
637 + }
638 +
639 + // Order by
640 + switch ( $filter->order_by ) {
641 + case 'price':
642 + case 'price_low':
643 + if ( 'price_low' === $filter->order_by ) {
644 + $filter->order = 'ASC';
645 + } else {
646 + $filter->order = 'DESC';
647 + }
648 +
649 + $filter = $lp_course_db->get_courses_order_by_price( $filter );
650 + break;
651 + case 'popular':
652 + $filter = $lp_course_db->get_courses_order_by_popular( $filter );
653 + break;
654 + case 'post_title':
655 + $filter->order = 'ASC';
656 + break;
657 + case 'post_title_desc':
658 + $filter->order_by = 'post_title';
659 + $filter->order = 'DESC';
660 + break;
661 + case 'menu_order':
662 + $filter->order_by = 'menu_order';
663 + $filter->order = 'ASC';
664 + break;
665 + default:
666 + $filter = apply_filters( 'lp/courses/filter/order_by/' . $filter->order_by, $filter );
667 + break;
668 + }
669 +
670 + // Query get results
671 + $filter = apply_filters( 'lp/courses/filter', $filter );
672 + $courses = LP_Course_DB::getInstance()->get_courses( $filter, $total_rows );
673 +
674 + //$lp_courses_cache->set_cache( $key_cache, json_encode( $courses ) );
675 + //$lp_courses_cache->set_cache( $key_cache_total_rows, $total_rows );
676 + } catch ( Throwable $e ) {
677 + $courses = [];
678 + error_log( __FUNCTION__ . ': ' . $e->getMessage() );
679 + }
680 +
681 + return $courses;
682 + }
683 +
684 + /**
685 + * Get full sections, items of course via Cache, extra info (if it has)
686 + *
687 + * @return array
688 + * @since 4.1.6.9
689 + * @version 1.0.1
690 + * @author tungnx
691 + */
692 + public function get_full_sections_and_items_course() {
693 + $sections_items = [];
694 + $course_id = $this->get_id();
695 +
696 + try {
697 + $courseModel = CourseModel::find( $course_id, true );
698 + return $courseModel->get_section_items();
699 +
700 + // Get cache
701 + $lp_course_cache = LP_Course_Cache::instance();
702 + $key_cache = "$course_id/sections_items";
703 + $sections_items = $lp_course_cache->get_cache( $key_cache );
704 +
705 + if ( ! $sections_items ) {
706 + $extra_info = $this->get_info_extra_for_fast_query();
707 +
708 + if ( empty( $extra_info->sections_items ) ) {
709 + $sections_items = $this->get_sections_and_items_course_from_db_and_sort();
710 + $extra_info->sections_items = $sections_items;
711 +
712 + // Save post meta
713 + $this->set_info_extra_for_fast_query( $extra_info );
714 + } else {
715 + $sections_items = $extra_info->sections_items;
716 + }
717 +
718 + $lp_course_cache->set_cache( $key_cache, $sections_items );
719 + }
720 + } catch ( Throwable $e ) {
721 + if ( LP_Debug::is_debug() ) {
722 + error_log( $e->getMessage() );
723 + }
724 + }
725 +
726 + return $sections_items;
727 + }
728 +
729 + /**
730 + * Get all sections and items from database, then handle sort
731 + * Only call when data change or not set
732 + *
733 + * @return array
734 + * @since 4.1.6.9
735 + * @version 1.0.0
736 + * @author tungnx
737 + */
738 + public function get_sections_and_items_course_from_db_and_sort(): array {
739 + $sections_items = [];
740 + $course_id = $this->get_id();
741 + $lp_course_db = LP_Course_DB::getInstance();
742 + $lp_course_cache = LP_Course_Cache::instance();
743 + $key_cache = "$course_id/sections_items";
744 +
745 + try {
746 + $sections_results = $lp_course_db->get_sections( $course_id );
747 + $sections_items_results = $lp_course_db->get_full_sections_and_items_course( $course_id );
748 + $count_items = count( $sections_items_results );
749 + $index_items_last = $count_items - 1;
750 + $section_current = 0;
751 +
752 + foreach ( $sections_items_results as $index => $sections_item ) {
753 + $section_new = $sections_item->section_id;
754 + $section_order = $sections_item->section_order;
755 + $item = new stdClass();
756 + $item->id = $sections_item->item_id;
757 + $item->order = $sections_item->item_order;
758 + $item->type = $sections_item->item_type;
759 +
760 + if ( $section_new != $section_current ) {
761 + $sections_items[ $section_new ] = new stdClass();
762 + $sections_items[ $section_new ]->id = $section_new; // old field will be deprecated in future
763 + $sections_items[ $section_new ]->section_id = $section_new; // new field
764 + $sections_items[ $section_new ]->order = $section_order; // old field will be deprecated in future
765 + $sections_items[ $section_new ]->section_order = $section_order; // new field
766 + $sections_items[ $section_new ]->title = html_entity_decode( $sections_item->section_name ); // old field will be deprecated in future
767 + $sections_items[ $section_new ]->section_name = html_entity_decode( $sections_item->section_name ); // new field
768 + $sections_items[ $section_new ]->description = html_entity_decode( $sections_item->section_description ); // old field will be deprecated in future
769 + $sections_items[ $section_new ]->section_description = html_entity_decode( $sections_item->section_description ); // new field
770 + $sections_items[ $section_new ]->items = [];
771 +
772 + // Sort item by item_order
773 + if ( $section_current != 0 ) {
774 + usort(
775 + $sections_items[ $section_current ]->items,
776 + function ( $item1, $item2 ) {
777 + return $item1->order - $item2->order;
778 + }
779 + );
780 + }
781 +
782 + $section_current = $section_new;
783 + }
784 +
785 + $sections_items[ $section_new ]->items[ $item->id ] = $item;
786 +
787 + if ( $index_items_last === $index ) {
788 + usort(
789 + $sections_items[ $section_current ]->items,
790 + function ( $item1, $item2 ) {
791 + return $item1->order - $item2->order;
792 + }
793 + );
794 + }
795 + }
796 +
797 + // Check case if section empty items
798 + foreach ( $sections_results as $section ) {
799 + $section_id = $section->section_id;
800 + if ( isset( $sections_items[ $section_id ] ) ) {
801 + continue;
802 + }
803 +
804 + $section_obj = new stdClass();
805 + $section_obj->id = $section_id;
806 + $section_obj->order = $section->section_order;
807 + $section_obj->title = html_entity_decode( $section->section_name );
808 + $section_obj->description = html_entity_decode( $section->section_description );
809 + $section_obj->items = [];
810 + $sections_items[ $section_id ] = $section_obj;
811 + }
812 +
813 + // Sort section by section_order
814 + usort(
815 + $sections_items,
816 + function ( $section1, $section2 ) {
817 + return $section1->order - $section2->order;
818 + }
819 + );
820 +
821 + $lp_course_cache->set_cache( $key_cache, $sections_items );
822 + } catch ( Throwable $e ) {
823 + if ( LP_Debug::is_debug() ) {
824 + error_log( $e->getMessage() );
825 + }
826 + }
827 +
828 + return $sections_items;
829 + }
830 +
831 + /**
832 + * Get sections of course.
833 + *
834 + * @param string $return.
835 + * @param int $section_id.
836 + *
837 + * @return array|bool|LP_Course_Section[]|LP_Course_Section
838 + * @version 4.0.0
839 + */
840 + public function get_sections( $return = 'object', $section_id = 0 ) {
841 + // $this->load_curriculum();
842 + // $sections = LP_Course_Utils::get_cached_db_sections( $this->get_id() );
843 +
844 + $sections_items = $this->get_full_sections_and_items_course();
845 +
846 + /*if ( false === $sections ) {
847 + return false;
848 + }*/
849 +
850 + //$position = 0;
851 + $sections = array();
852 + foreach ( $sections_items as $k => $section_items ) {
853 + $position = $k + 1;
854 + $section_items_tmp = [
855 + 'section_id' => $section_items->id,
856 + 'section_name' => $section_items->title,
857 + 'section_course_id' => $this->get_id(),
858 + 'section_order' => $section_items->order,
859 + 'section_description' => $section_items->description,
860 + 'items' => $section_items->items,
861 + ];
862 + $sid = $section_items->id;
863 + $section = new LP_Course_Section( $section_items_tmp );
864 + $section->set_position( $position );
865 + $sections[ $sid ] = $section;
866 + }
867 +
868 + if ( $section_id ) {
869 + $sections = $sections[ $section_id ] ?? [];
870 + }
871 +
872 + return apply_filters( 'learn-press/course-sections', $sections, $this->get_id(), $return, $section_id );
873 + }
874 +
875 + /**
876 + * Get sections arr
877 + *
878 + * @param int $section_id
879 + *
880 + * @return array
881 + * @version 1.0.0
882 + * @sicne 4.1.7
883 + */
884 + public function get_sections_data_arr( int $section_id = 0 ): array {
885 + $sections_items = $this->get_full_sections_and_items_course();
886 + $sections = array();
887 +
888 + foreach ( $sections_items as $section_items ) {
889 + $section = [
890 + 'section_id' => $section_items->id,
891 + 'section_name' => $section_items->title,
892 + 'section_course_id' => $this->get_id(),
893 + 'section_order' => $section_items->order,
894 + 'section_description' => $section_items->description,
895 + 'items' => $section_items->items,
896 + ];
897 +
898 + if ( $section_id && $section_id == $section['section_id'] ) {
899 + $sections = $section;
900 + break;
901 + }
902 +
903 + $sections[] = $section;
904 + }
905 +
906 + return $sections;
907 + }
908 +
909 + /**
910 + * Get raw data curriculum.
911 + *
912 + * @return array
913 + * @since 3.0.0
914 + * @editor tungnx
915 + * @version 1.0.1
916 + */
917 + public function get_curriculum_raw(): array {
918 + $sections_data = array();
919 +
920 + $sections_items = $this->get_full_sections_and_items_course();
921 + foreach ( $sections_items as $section_items ) {
922 + $section_items_arr = (array) $section_items;
923 + $section_items_arr['course_id'] = $this->get_id();
924 + $section_items_arr['items'] = [];
925 +
926 + foreach ( $section_items->items as $item ) {
927 + $itemObject = $this->get_item( $item->id );
928 +
929 + if ( ! $itemObject instanceof LP_Course_Item ) {
930 + continue;
931 + }
932 +
933 + $item_arr = (array) $item;
934 + $item_arr['title'] = html_entity_decode( $itemObject->get_title() );
935 + $item_arr['preview'] = $itemObject->is_preview();
936 + $section_items_arr['items'][] = apply_filters( 'learn-press/item/to_array', $item_arr );
937 + }
938 +
939 + $sections_data[] = $section_items_arr;
940 + }
941 +
942 + return $sections_data;
943 + }
944 +
945 + /**
946 + * Get all curriculum of this course.
947 + *
948 + * @param int $section_id
949 + * @param bool $force
950 + *
951 + * @return bool|LP_Course_Section[]
952 + */
953 + public function get_curriculum( $section_id = 0, $force = false ) {
954 + return $this->get_sections( 'object', $section_id );
955 + }
956 +
957 + /**
958 + * Return list of item's ids in course's curriculum.
959 + *
960 + * @param string|array $type
961 + *
962 + * @return array
963 + * @since 3.0.0
964 + * @version 3.0.2
965 + */
966 + public function get_items( $type = '' ) {
967 + //$this->load();
968 +
969 + $sections_items = $this->get_full_sections_and_items_course();
970 +
971 + $items = array();
972 +
973 + foreach ( $sections_items as $section_items ) {
974 + foreach ( $section_items->items as $item ) {
975 + if ( ! empty( $type ) ) {
976 + if ( $type === $item->type ) {
977 + $items[] = $item->id;
978 + }
979 + } else {
980 + $items[] = $item->id;
981 + }
982 + }
983 + }
984 +
985 + return $items;
986 + }
987 +
988 + /**
989 + * Get all items in a course.
990 + *
991 + * @param string $type . Type of items, eg: lp_lesson, lp_quiz...
992 + *
993 + * @return array
994 + * @deprecated
995 + */
996 + public function get_curriculum_items( $type = '' ) {
997 + return $this->get_items( $type );
998 + }
999 +
1000 + /**
1001 + * Get evaluation type
1002 + *
1003 + * @since 4.2.1
1004 + * @version 1.0.0
1005 + * @return string
1006 + */
1007 + public function get_evaluation_type(): string {
1008 + $evaluation_type = get_post_meta( $this->get_id(), '_lp_course_result', true );
1009 + if ( ! $evaluation_type ) {
1010 + $evaluation_type = 'evaluate_lesson';
1011 + }
1012 +
1013 + return $evaluation_type;
1014 + }
1015 +
1016 + /**
1017 + * Get categories of course.
1018 + *
1019 + * @since 4.2.3
1020 + * @version 1.0.0
1021 + * @return array|WP_Term[]
1022 + */
1023 + public function get_categories(): array {
1024 + // Todo: set cache.
1025 + $categories = get_the_terms( $this->get_id(), LP_COURSE_CATEGORY_TAX );
1026 + if ( ! $categories ) {
1027 + $categories = array();
1028 + }
1029 +
1030 + return $categories;
1031 + }
1032 +
1033 + /**
1034 + * Get tags of course.
1035 + *
1036 + * @since 4.2.3
1037 + * @version 1.0.0
1038 + * @return array|WP_Term[]
1039 + */
1040 + public function get_tags(): array {
1041 + // Todo: set cache.
1042 + $tags = get_the_terms( $this->get_id(), LP_COURSE_TAXONOMY_TAG );
1043 + if ( ! $tags ) {
1044 + $tags = array();
1045 + }
1046 +
1047 + return $tags;
1048 + }
1049 +
1050 + /**
1051 + * Get all categories.
1052 + *
1053 + * @return array
1054 + */
1055 + public static function get_all_categories(): array {
1056 + // Todo: set cache.
1057 + $categories = get_terms( LP_COURSE_CATEGORY_TAX );
1058 + if ( ! $categories ) {
1059 + $categories = array();
1060 + }
1061 +
1062 + return $categories;
1063 + }
1064 +
1065 + /**
1066 + * Get all curriculum of this course.
1067 + *
1068 + * @return bool|LP_Course_Section
1069 + * @since 4.2.3.5
1070 + */
1071 + public function get_level(): string {
1072 + $level = get_post_meta( $this->get_id(), '_lp_level', true );
1073 + if ( ! $level ) {
1074 + $level = '';
1075 + }
1076 +
1077 + return $level;
1078 + }
1079 +
1080 + /**
1081 + * Get duration of this course.
1082 + *
1083 + * @return bool|LP_Course_Section
1084 + * @since 4.2.3.5
1085 + */
1086 + public function get_duration(): string {
1087 + $duration = get_post_meta( $this->get_id(), '_lp_duration', true );
1088 +
1089 + return $duration;
1090 + }
1091 +
1092 + /**
1093 + * Check if a course is enabled Offline
1094 + *
1095 + * @return bool
1096 + */
1097 + public function is_offline(): bool {
1098 + return get_post_meta( $this->get_id(), CoursePostModel::META_KEY_OFFLINE_COURSE, 'no' ) === 'yes';
1099 + }
1100 + }
1101 +}