PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.3.8
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.3.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 4.2.1 All 138 releases
learnpress / inc / course / class-lp-course.php

class-lp-course.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.3.8, at inc/course/class-lp-course.php

1,102 lines 32.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
1102