PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.3.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.3.9
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 / abstract-course.php

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

1,475 lines 38.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class LP_Abstract_Course.
4 *
5 * @author ThimPress
6 * @package LearnPress/Classes
7 * @version 4.0.0
8 */
9
10 use LearnPress\Models\CourseModel;
11 use LearnPress\TemplateHooks\Course\SingleCourseTemplate;
12 use LearnPress\TemplateHooks\Instructor\SingleInstructorTemplate;
13
14 defined( 'ABSPATH' ) || exit();
15
16 if ( ! function_exists( 'LP_Abstract_Course' ) ) {
17
18 /**
19 * Class LP_Abstract_Course.
20 */
21 abstract class LP_Abstract_Course extends LP_Abstract_Post_Data {
22 /**
23 * Course type
24 *
25 * @var string .
26 */
27 public $course_type = null;
28
29 /**
30 * @var LP_Course_CURD|null
31 */
32 protected $_curd = null;
33
34 /**
35 * Post type
36 *
37 * @var string
38 */
39 protected $_post_type = LP_COURSE_CPT;
40
41 /**
42 * @var array
43 */
44 protected $_data = array(
45 'status' => '',
46 'require_enrollment' => '',
47 'price' => '',
48 'regular_price' => '',
49 'sale_price' => '',
50 'sale_start' => '',
51 'sale_end' => '',
52 'duration' => 0,
53 'max_students' => 0,
54 'students' => 0,
55 'retake_count' => 0,
56 'featured' => '',
57 'course_result' => '',
58 'passing_conditional' => '',
59 'external_link' => '',
60 'payment' => '',
61 );
62
63 protected $_loaded = false;
64
65 /**
66 * @var int
67 */
68 protected $user_id = 0;
69
70 public function set_user( $user ) {
71 if ( is_numeric( $user ) ) {
72 $this->user_id = absint( $user );
73 } elseif ( $user instanceof LP_User ) {
74 $this->user_id = $user->get_id();
75 }
76 }
77
78 public function get_user( $return = 'id' ) {
79 if ( $return === 'id' ) {
80 return $this->user_id;
81 }
82
83 if ( $this->user_id ) {
84 return learn_press_get_user( $this->user_id );
85 }
86
87 return false;
88 }
89
90 /**
91 * Constructor gets the post object and sets the ID for the loaded course.
92 *
93 * @param mixed $the_course Course ID, post object, or course object
94 * @param mixed $deprecated Deprecated
95 */
96 public function __construct( $the_course, $deprecated = '' ) {
97
98 $this->_curd = new LP_Course_CURD();
99
100 if ( is_numeric( $the_course ) && $the_course > 0 ) {
101 $this->set_id( $the_course );
102 } elseif ( $the_course instanceof self ) {
103 $this->set_id( absint( $the_course->get_id() ) );
104 } elseif ( ! empty( $the_course->ID ) ) {
105 $this->set_id( absint( $the_course->ID ) );
106 }
107
108 if ( $this->get_id() > 0 ) {
109
110 }
111 }
112
113 public function refresh() {
114 $this->_loaded = false;
115 if ( $this->get_id() > 0 ) {
116 $this->load();
117 }
118 }
119
120 /**
121 * Read course data.
122 * - Curriculum: sections, items, etc...
123 *
124 * Todo: optimize this function
125 *
126 * @since 3.0.0
127 * @editor tungnx
128 * @version 1.0.1
129 */
130 public function load() {
131 if ( $this->_loaded ) {
132 return;
133 }
134
135 $this->load_data();
136
137 /*$can_load_curriculum = false;
138 // Check if edit course, single course, single item can be load
139 if ( in_array( LP_Page_Controller::page_current(), array( LP_PAGE_SINGLE_COURSE, LP_PAGE_SINGLE_COURSE_CURRICULUM ) ) ) {
140 $can_load_curriculum = true;
141 } elseif ( is_admin() && is_callable( 'get_current_screen' ) ) {
142 $current_screen = get_current_screen();
143 if ( $current_screen && LP_COURSE_CPT === $current_screen->id ) {
144 $can_load_curriculum = true;
145 }
146 } elseif ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
147 if ( isset( $_REQUEST['sectionID'] ) || isset( $_REQUEST['sectionId'] ) ) {
148 $can_load_curriculum = true;
149 }
150 }
151
152 if ( $can_load_curriculum ) {
153 $this->load_curriculum();
154 }*/
155
156 $this->_loaded = true;
157 }
158
159 public function load_data() {
160 $id = $this->get_id();
161 $post_object = get_post( $id );
162
163 // Regular price
164 $regular_price = get_post_meta( $id, '_lp_price', true ); // For LP version < 1.4.1.2
165 if ( metadata_exists( 'post', $this->get_id(), '_lp_regular_price' ) ) {
166 $regular_price = get_post_meta( $id, '_lp_regular_price', true );
167 }
168
169 $block_course_finished = get_post_meta(
170 $id,
171 '_lp_block_finished',
172 true
173 );
174
175 $this->_set_data(
176 array(
177 'status' => $post_object->post_status,
178 'no_required_enroll' => get_post_meta( $id, '_lp_no_required_enroll', true ),
179 'price' => get_post_meta( $id, '_lp_price', true ),
180 'regular_price' => $regular_price,
181 'sale_price' => get_post_meta( $id, '_lp_sale_price', true ),
182 'sale_start' => get_post_meta( $id, '_lp_sale_start', true ),
183 'sale_end' => get_post_meta( $id, '_lp_sale_end', true ),
184 'duration' => get_post_meta( $id, '_lp_duration', true ),
185 'max_students' => get_post_meta( $id, '_lp_max_students', true ),
186 'students' => false,
187 'fake_students' => get_post_meta( $id, '_lp_students', true ),
188 'retake_count' => get_post_meta( $id, '_lp_retake_count', true ),
189 'featured' => get_post_meta( $id, '_lp_featured', true ),
190 'course_result' => get_post_meta( $id, '_lp_course_result', true ),
191 'passing_condition' => get_post_meta( $id, '_lp_passing_condition', true ),
192 'final_quiz' => get_post_meta( $id, '_lp_final_quiz', true ),
193 'external_link' => get_post_meta( $id, '_lp_external_link_buy_course', true ),
194 'block_course_duration_expire' => get_post_meta(
195 $id,
196 '_lp_block_expire_duration',
197 true
198 ),
199 'block_course_finished' => $block_course_finished ? $block_course_finished : 'yes',
200 'allow_repurchase' => get_post_meta( $id, '_lp_allow_course_repurchase', true ),
201 'allow_repurchase_course_option' => get_post_meta( $id, '_lp_course_repurchase_option', true ),
202 'excerpt' => $post_object->post_excerpt,
203 )
204 );
205 }
206
207 /**
208 * Load course curriculum.
209 *
210 * @deprecated 4.1.6.9
211 */
212 public function load_curriculum() {
213 _deprecated_function( __FUNCTION__, '4.1.6.9' );
214 /*$item_ids = array();
215 $item_types = array();
216 $item_by_types = array();
217 $section_items = array();
218
219 $items = $this->_curd->read_course_curriculum( $this->get_id() );
220 if ( $items ) {
221 foreach ( $items as $item ) {
222 $item_ids[] = $item->id;
223
224 // Group items by it type
225 if ( empty( $item_types[ $item->type ] ) ) {
226 $item_types[ $item->type ] = array();
227 }
228
229 $item_types[ $item->type ][] = $item->id;
230
231 // Group items by it section
232 if ( empty( $section_items[ $item->section_id ] ) ) {
233 $section_items[ $item->section_id ] = array();
234 }
235
236 $section_items[ $item->section_id ][] = $item->id;
237 $item_by_types[ $item->id ] = $item->type;
238
239 }
240 }
241
242 LP_Course_Utils::set_course_items( $this->get_id(), $item_ids );
243 LP_Course_Utils::set_course_item_types( $this->get_id(), $item_by_types );
244 LP_Course_Utils::set_course_items_group_types( $this->get_id(), $item_types );
245
246 foreach ( $section_items as $section_id => $its ) {
247 LP_Course_Utils::set_section_items( $section_id, $its );
248 }*/
249 }
250
251 /**
252 * __isset function.
253 *
254 * @param mixed $key
255 *
256 * @return bool
257 */
258 public function __isset( $key ) {
259 return metadata_exists( 'post', $this->get_id(), '_' . $key );
260 }
261
262 /**
263 * __get function.
264 *
265 * @param string $key
266 *
267 * @return mixed
268 */
269 public function __get( $key ) {
270 _deprecated_argument( __CLASS__ . '::' . $key, '3.0.11' );
271
272 return false;
273 }
274
275 /**
276 * Get course thumbnail, return placeholder if it does not exists
277 *
278 * @param string $size
279 * @param array $attr
280 *
281 * @return string
282 */
283 public function get_image( string $size = 'course_thumbnail', array $attr = array() ): string {
284 $image = LP_Thumbnail_Helper::instance()->get_course_image( $this->get_id(), $size, $attr );
285
286 return apply_filters( 'learn-press/course/image', $image, $this->get_id(), $size, $attr );
287 }
288
289 /**
290 * Get course thumbnail, return placeholder if it does not exists
291 *
292 * @param string $size
293 *
294 * @return string
295 */
296 public function get_image_url( $size = 'course_thumbnail' ) {
297 $course_id = $this->get_id();
298 $url = '';
299 $parent_id = wp_get_post_parent_id( $course_id );
300
301 if ( has_post_thumbnail( $course_id ) ) {
302 $url = get_the_post_thumbnail_url( $course_id, $size );
303 } elseif ( $parent_id && has_post_thumbnail( $parent_id ) ) {
304 $url = get_the_post_thumbnail_url( $parent_id, $size );
305 }
306
307 if ( ! $url ) {
308 $url = LearnPress::instance()->image( 'no-image.png' );
309 }
310
311 return apply_filters( 'learn-press/course-thumbnail-url', $url, $this->get_id(), $size );
312 }
313
314 /**
315 * @return false|string
316 */
317 public function get_permalink() {
318 return get_the_permalink( $this->get_id() );
319 }
320
321 /**
322 * Course is exists if the post is not empty
323 *
324 * @return bool
325 */
326 public function exists() {
327 return LP_COURSE_CPT === learn_press_get_post_type( $this->get_id() );
328 }
329
330 /**
331 * @return bool
332 */
333 public function is_publish() {
334 return 'publish' === get_post_status( $this->get_id() );
335 }
336
337 /**
338 * Check this course is required enroll.
339 *
340 * @return bool
341 */
342 public function is_required_enroll(): bool {
343 return ! $this->is_no_required_enroll();
344 }
345
346 /**
347 * @deprecated 4.2.0
348 */
349 public function is_require_enrollment() {
350 _deprecated_function( __METHOD__, '4.2.0' );
351 return ! $this->is_no_required_enroll();
352 }
353
354 /**
355 * Check if this course is required enroll or not.
356 *
357 * @author hungkv
358 * @since 4.0.5
359 * @return bool
360 * @version 1.0.1
361 */
362 public function is_no_required_enroll(): bool {
363 return $this->get_data( 'no_required_enroll', 'no' ) === 'yes' && ! is_user_logged_in();
364 }
365
366 /**
367 * @deprecated 4.1.6.9
368 */
369 /*public function get_item_types( $group = false ) {
370 $cache_key = $group ? 'course-item-group-types' : 'course-item-types';
371 $items = LP_Object_Cache::get( 'course-' . $this->get_id(), "learn-press/{$cache_key}" );
372 $items = false;
373
374 if ( false === $items ) {
375 $item_types = array();
376 $items = array();
377 $sections = array();
378 $all_items = $this->_curd->read_course_items( $this->get_id() );
379
380 if ( $all_items ) {
381 foreach ( $all_items as $item ) {
382 if ( empty( $item_types[ $item->type ] ) ) {
383 $item_types[ $item->type ] = array();
384 }
385 $item_types[ $item->type ][] = $item->id;
386 $items[ $item->id ] = $item->type;
387
388 if ( empty( $sections[ $item->section_id ] ) ) {
389 $sections[ $item->section_id ] = array();
390 }
391 $sections[ $item->section_id ][] = $item->id;
392 }
393 }
394
395 LP_Object_Cache::set( 'course-' . $this->get_id(), $item_types, 'learn-press/course-item-group-types' );
396 LP_Object_Cache::set( 'course-' . $this->get_id(), $items, 'learn-press/course-item-types' );
397
398 foreach ( $sections as $section_id => $section_items ) {
399 LP_Object_Cache::set( 'section-' . $section_id, $section_items, 'learn-press/section-items' );
400 }
401
402 $items = $group ? $item_types : $items;
403 }
404
405 return apply_filters( "learn-press/{$cache_key}", $items, $this->get_id() );
406 }*/
407
408 /**
409 * Get all item's ids in a course.
410 *
411 * @param int $section_id
412 *
413 * @return array
414 * @version 4.0.1
415 * @modify 4.1.6.9 tungnx
416 */
417 public function get_item_ids( int $section_id = 0 ): array {
418 $item_ids = array();
419
420 $sections_items = $this->get_full_sections_and_items_course();
421 foreach ( $sections_items as $section_items ) {
422 foreach ( $section_items->items as $item ) {
423 if ( $section_id ) {
424 if ( $section_id == $section_items->id ) {
425 $item_ids[] = $item->id;
426 }
427
428 continue;
429 }
430
431 $item_ids[] = $item->id;
432 }
433 }
434
435 return apply_filters( 'learn-press/course-item-ids', $item_ids, $this->get_id() );
436 }
437
438 /**
439 * Get item is viewing in single course.
440 *
441 * @return LP_Course_Item
442 * @deprecated 4.1.6.9
443 */
444 public function get_viewing_item() {
445 _deprecated_function( __FUNCTION__, '4.1.6.9' );
446 //return apply_filters( 'learn-press/single-course-viewing-item', $this->_viewing_item, $this->get_id() );
447 }
448
449 /**
450 * Get total students fake.
451 *
452 * @return int
453 */
454 public function get_fake_students(): int {
455 return absint( $this->get_data( 'fake_students', 0 ) );
456 }
457
458 /**
459 * Count the real users has enrolled
460 *
461 * @return int
462 */
463 public function get_users_enrolled() {
464 $enrolled = $this->get_data( 'students' );
465
466 if ( false === $enrolled ) {
467 $enrolled = $this->count_students();
468
469 $this->_set_data( 'students', $enrolled );
470 }
471
472 $enrolled = absint( $enrolled );
473
474 return apply_filters( 'learn-press/course/users-enrolled', $enrolled, $this );
475 }
476
477 /**
478 * @param string $field
479 *
480 * @return LP_User|mixed
481 */
482 public function get_instructor( $field = '' ) {
483 $user = learn_press_get_user( get_post_field( 'post_author', $this->get_id() ) );
484
485 return $field ? $user->get_data( $field ) : $user;
486 }
487
488 /**
489 * @return mixed
490 */
491 public function get_instructor_name() {
492 $instructor = $this->get_instructor();
493 $name = '';
494
495 if ( $instructor ) {
496 if ( $instructor->get_data( 'display_name' ) ) {
497 $name = $instructor->get_data( 'display_name' );
498 } elseif ( $instructor->get_data( 'user_nicename' ) ) {
499 $name = $instructor->get_data( 'user_nicename' );
500 } elseif ( $instructor->get_data( 'user_login' ) ) {
501 $name = $instructor->get_data( 'user_login' );
502 }
503 }
504
505 return apply_filters( 'learn-press/course/instructor-name', $name, $this->get_id() );
506 }
507
508 /**
509 * Get instructor html of course.
510 *
511 * @param int|bool $with_avatar
512 * @param string $link_class
513 *
514 * @return string
515 */
516 public function get_instructor_html( $with_avatar = false, $link_class = '' ): string {
517 $html = '';
518
519 try {
520 $instructor = $this->get_author();
521 if ( ! $instructor ) {
522 return '';
523 }
524
525 $singleInstructorTemplate = SingleInstructorTemplate::instance();
526
527 $html = apply_filters(
528 'learn-press/course/instructor-html',
529 sprintf(
530 '<a href="%s"%s>%s %s</a>',
531 $instructor->get_url_instructor(),
532 $link_class ? sprintf( 'class="%s"', $link_class ) : '',
533 $with_avatar ? $instructor->get_profile_picture() : '',
534 $singleInstructorTemplate->html_display_name( $instructor )
535 ),
536 $instructor,
537 $singleInstructorTemplate
538 );
539 } catch ( Throwable $e ) {
540 error_log( $e->getMessage() );
541 }
542
543 return $html;
544 }
545
546 /**
547 * Check if a course is Free
548 *
549 * @return bool
550 */
551 public function is_free(): bool {
552 return apply_filters( 'learn-press/course/is-free', $this->get_price() == 0, $this->get_id() );
553 }
554
555 /**
556 * Get the sale price of course. Check if sale price is set
557 * and the dates are valid.
558 *
559 * @return string|float
560 */
561 public function get_sale_price() {
562 $sale_price_value = $this->get_data( 'sale_price', '' );
563
564 if ( '' !== $sale_price_value ) {
565 return apply_filters( 'learn-press/course/sale-price', floatval( $sale_price_value ), $this->get_id() );
566 }
567
568 return $sale_price_value;
569 }
570
571 /**
572 * Check course has 'sale price'
573 *
574 * @return mixed
575 */
576 public function has_sale_price() {
577 $has_sale_price = false;
578 $regular_price = $this->get_regular_price();
579 $sale_price = $this->get_sale_price();
580 $start_date = $this->get_data( 'sale_start', '' );
581 $end_date = $this->get_data( 'sale_end', '' );
582
583 if ( $regular_price > $sale_price && is_float( $sale_price ) ) {
584 $has_sale_price = true;
585 }
586
587 // Check in days sale
588 if ( $has_sale_price && '' !== $start_date && '' !== $end_date ) {
589 $nowObj = new LP_Datetime();
590 // Compare via timezone WP
591 $nowStr = $nowObj->toSql( true );
592 $now = strtotime( $nowStr );
593 $end = strtotime( $end_date );
594 $start = strtotime( $start_date );
595
596 $has_sale_price = $now >= $start && $now <= $end;
597 }
598
599 return apply_filters( 'learn-press/course/has-sale-price', $has_sale_price, $this->get_id() );
600 }
601
602 /**
603 * Get the regular price of course.
604 *
605 * @return float
606 */
607 public function get_regular_price(): float {
608 $price = floatval( $this->get_data( 'regular_price', 0 ) );
609
610 return apply_filters( 'learn-press/course/regular-price', $price, $this->get_id() );
611 }
612
613 /**
614 * Get the regular price format of course.
615 *
616 * @since 4.1.5
617 * @version 1.0.0
618 * @author tungnx
619 * @return mixed
620 */
621 public function get_regular_price_html() {
622 $price = learn_press_format_price( $this->get_regular_price(), true );
623
624 return apply_filters( 'learn-press/course/regular-price-html', $price, $this->get_id() );
625 }
626
627 /**
628 * Get the price of course.
629 *
630 * @return mixed
631 */
632 public function get_price() {
633 $key_cache = "{$this->get_id()}/price";
634 $price = LP_Course_Cache::cache_load_first( 'get', $key_cache );
635
636 if ( false === $price ) {
637 if ( $this->has_sale_price() ) {
638 $price = $this->get_sale_price();
639 // Add key _lp_course_is_sale for query - Todo: Check performance, need write function get all courses, and set on Admin, on background
640 //update_post_meta( $this->get_id(), '_lp_course_is_sale', 1 );
641 } else {
642 // Delete key _lp_course_is_sale
643 //delete_post_meta( $this->get_id(), '_lp_course_is_sale' );
644 $price = $this->get_regular_price();
645 }
646
647 // Save price only on page Single Course
648 /*if ( LP_PAGE_SINGLE_COURSE === LP_Page_Controller::page_current() ) {
649 update_post_meta( $this->get_id(), '_lp_price', $price );
650 }*/
651
652 LP_Course_Cache::cache_load_first( 'set', $key_cache, $price );
653 }
654
655 return apply_filters( 'learn-press/course/price', $price, $this->get_id() );
656 }
657
658 /**
659 * Get html course price
660 *
661 * @author tungnx
662 * @since 4.1.5
663 * @version 1.0.2
664 * @return string
665 */
666 public function get_course_price_html(): string {
667 $price_html = '';
668
669 if ( $this->is_free() ) {
670 if ( is_float( $this->get_sale_price() ) ) {
671 $price_html .= sprintf( '<span class="origin-price">%s</span>', $this->get_regular_price_html() );
672 }
673
674 $price_html .= sprintf( '<span class="free">%s</span>', esc_html__( 'Free', 'learnpress' ) );
675 $price_html = apply_filters( 'learn_press_course_price_html_free', $price_html, $this );
676 } elseif ( $this->get_data( 'no_required_enroll', 'no' ) === 'yes' ) {
677 $price_html .= '';
678 } else {
679 if ( $this->has_sale_price() ) {
680 $price_html .= sprintf( '<span class="origin-price">%s</span>', $this->get_regular_price_html() );
681 }
682
683 $price_html .= sprintf( '<span class="price">%s</span>', learn_press_format_price( $this->get_price(), true ) );
684 $course = CourseModel::find( $this->get_id(), true );
685 $singleCourseTemplate = SingleCourseTemplate::instance();
686 $price_html = sprintf(
687 '%1$s %2$s %3$s',
688 $singleCourseTemplate->html_price_prefix( $course ),
689 $price_html,
690 $singleCourseTemplate->html_price_suffix( $course )
691 );
692 $price_html = apply_filters( 'learn_press_course_price_html', $price_html, $this->has_sale_price(), $this->get_id() );
693 }
694
695 return sprintf( '<span class="course-item-price">%s</span>', $price_html );
696 }
697
698 /**
699 * Get the price of course with html
700 *
701 * @editor tungnx
702 * @modify 4.1.5
703 * @version 1.0.1
704 * @return mixed
705 * @deprecated 4.1.5
706 */
707 public function get_price_html() {
708 $price_html = '';
709
710 if ( $this->is_free() ) {
711 $price_html = apply_filters(
712 'learn_press_course_price_html_free',
713 esc_html__( 'Free', 'learnpress' ),
714 $this
715 );
716 } else {
717 $price_html .= learn_press_format_price( $this->get_price() );
718 $price_html = apply_filters( 'learn_press_course_price_html', $price_html, $this->has_sale_price(), $this->get_id() );
719 }
720
721 return $price_html;
722 }
723
724 /**
725 * Get the origin price of course
726 *
727 * @return float
728 * @deprecated 4.1.5
729 */
730 public function get_origin_price() {
731 return $this->get_regular_price();
732 }
733
734 /**
735 * Get the price of course with html
736 *
737 * @return mixed
738 * @deprecated 4.1.5
739 */
740 public function get_origin_price_html() {
741 return $this->get_regular_price_html();
742 }
743
744 /**
745 * @param bool $item_id
746 *
747 * @return bool|mixed
748 */
749 public function is_viewing_item( $item_id = false ) {
750 $item = LP_Global::course_item();
751
752 if ( empty( $item ) ) {
753 return false;
754 }
755
756 return apply_filters(
757 'learn-press/is-viewing-item',
758 false !== $item_id ? $item_id == $item->get_id() : $item->get_id(),
759 $item_id,
760 $this->get_id()
761 );
762 }
763
764 /**
765 * @param $item_id
766 *
767 * @return bool|mixed
768 */
769 public function is_current_item( $item_id ) {
770 return $this->is_viewing_item( $item_id );
771 }
772
773 /**
774 * Check if the course has 'feature'
775 * This function call to a function with prefix 'has'
776 *
777 * @param string
778 *
779 * @return mixed
780 * @throws Exception
781 */
782 public function has( $tag ) {
783 _deprecated_function( __FUNCTION__, '3.0.8' );
784
785 $args = func_get_args();
786 unset( $args[0] );
787 $method = 'has_' . preg_replace( '!-!', '_', $tag );
788 $callback = array( $this, $method );
789
790 if ( is_callable( $callback ) ) {
791 return call_user_func_array( $callback, $args );
792 } else {
793 throw new Exception( sprintf( __( 'The function %s doesn\'t exist', 'learnpress' ), $tag ) );
794 }
795 }
796
797 /**
798 * @param string
799 *
800 * @return mixed
801 * @throws Exception
802 */
803 public function is( $tag ) {
804 _deprecated_function( __FUNCTION__, '3.0.8' );
805 $args = func_get_args();
806 unset( $args[0] );
807 $method = 'is_' . preg_replace( '!-!', '_', $tag );
808 $callback = array( $this, $method );
809
810 if ( is_callable( $callback ) ) {
811 return call_user_func_array( $callback, $args );
812 } else {
813 throw new Exception( sprintf( __( 'The function %s doesn\'t exist', 'learnpress' ), $tag ) );
814 }
815 }
816
817 /**
818 * Return true if this course can be purchasable.
819 * Required enroll.
820 * Status is publish.
821 *
822 * @return mixed
823 */
824 public function is_purchasable() {
825 $is_purchasable = $this->exists() && ! $this->is_no_required_enroll() && get_post_status( $this->get_id() ) == 'publish';
826
827 return apply_filters( 'learn-press/is-purchasable', $is_purchasable, $this->get_id() );
828 }
829
830 /**
831 * Check if students have purchased course is reached.
832 * For case check course can purchase.
833 *
834 * @return mixed
835 * @since 3.0.0
836 * @version 1.0.1
837 */
838 public function is_in_stock() {
839 $in_stock = true;
840 $max_allowed = $this->get_max_students();
841
842 if ( $max_allowed ) {
843 $in_stock = $max_allowed > $this->get_total_user_enrolled_or_purchased();
844 }
845
846 return apply_filters( 'learn-press/is-in-stock', $in_stock, $this->get_id() );
847 }
848
849 /**
850 * Check max student can enroll.
851 * For case check course can enroll.
852 *
853 * @return mixed
854 * @since 4.2.5.7
855 * @version 1.0.0
856 */
857 public function is_in_stock_enroll() {
858 $in_stock = true;
859 $max_allowed = $this->get_max_students();
860
861 if ( $max_allowed ) {
862 $in_stock = $max_allowed > $this->get_total_user_enrolled_or_purchased();
863 }
864
865 return apply_filters( 'learn-press/is-in-stock', $in_stock, $this->get_id() );
866 }
867
868 /**
869 * Get max students can enroll to course.
870 *
871 * @return int
872 *
873 * @since 3.0.0
874 */
875 public function get_max_students() {
876 return apply_filters(
877 'learn-press/max-students',
878 absint( $this->get_data( 'max_students' ) ),
879 $this->get_id()
880 );
881 }
882
883 /**
884 * Count number of students enrolled course.
885 * Check global settings `enrolled_students_number`
886 * and add the fake value if both are set.
887 *
888 * @return int
889 * @editor tungnx
890 * @version 1.0.1
891 * @since 3.0.0
892 * @Todo: view and rewrite this function
893 */
894 public function count_students(): int {
895 $total = $this->get_total_user_enrolled_or_purchased();
896 $total += $this->get_fake_students();
897
898 return $total;
899 }
900
901 /**
902 * Get total user enrolled
903 *
904 * @since 4.1.4
905 * @version 1.0.1
906 * @return int
907 */
908 public function get_total_user_enrolled(): int {
909 $total = 0;
910 $lp_course_cache = new LP_Course_Cache( true );
911
912 try {
913 $total = $lp_course_cache->get_total_students_enrolled( $this->get_id() );
914 if ( false !== $total ) {
915 return $total;
916 }
917
918 $lp_course_db = LP_Course_DB::getInstance();
919 $total = $lp_course_db->get_total_user_enrolled( $this->get_id() );
920 $lp_course_cache->set_total_students_enrolled( $this->get_id(), $total );
921 } catch ( Throwable $e ) {
922 error_log( $e->getMessage() );
923 }
924
925 return $total;
926 }
927
928 /**
929 * Get total user enrolled include fake
930 * @since 4.2.2
931 * @return int
932 */
933 /*public function get_total_user_enrolled_include_fake() {
934 $total_user_enrolled = 0;
935 $key_cache = "{$this->get_id()}/total-students-include-fake";
936
937 try {
938 $total_user_enrolled = LP_Course_Cache::cache_load_first( 'get', $key_cache );
939
940 if ( false === $total_user_enrolled ) {
941 $lp_course_db = LP_Course_DB::getInstance();
942 $total_user_enrolled = $lp_course_db->get_total_user_enrolled( $this->get_id() );
943 $total_user_enrolled += $this->get_fake_students();
944
945 LP_Course_Cache::cache_load_first( 'set', $key_cache, $total_user_enrolled );
946 }
947 } catch ( Throwable $e ) {
948 error_log( $e->getMessage() );
949 }
950
951 return $total_user_enrolled;
952 }*/
953
954 /**
955 * Get total user enrolled, purchased or finished
956 *
957 * @since 4.1.4
958 * @version 1.0.1
959 * @return int
960 */
961 public function get_total_user_enrolled_or_purchased(): int {
962 $total = 0;
963 $lp_course_cache = new LP_Course_Cache( true );
964
965 try {
966 $total = $lp_course_cache->get_total_students_enrolled_or_purchased( $this->get_id() );
967 if ( false !== $total ) {
968 return $total;
969 }
970
971 $lp_course_db = LP_Course_DB::getInstance();
972 $total = $lp_course_db->get_total_user_enrolled_or_purchased( $this->get_id() );
973 $lp_course_cache->set_total_students_enrolled_or_purchased( $this->get_id(), $total );
974 } catch ( Throwable $e ) {
975 error_log( $e->getMessage() );
976 }
977
978 return $total;
979 }
980
981 /**
982 * @param string|array $statuses
983 *
984 * @return mixed
985 */
986 public function count_in_order( $statuses = 'completed' ) {
987 settype( $statuses, 'array' );
988 $count = 0;
989
990 foreach ( $statuses as $status ) {
991 $orders = get_post_meta( $this->get_id(), 'order-' . $status, true );
992
993 if ( $orders ) {
994 $count += sizeof( $orders );
995 }
996 }
997
998 return $count;
999 }
1000
1001 /**
1002 * @editor tungnx
1003 * @modify 4.1.4 - comment - not use
1004 */
1005 /*public function count_completed_orders() {
1006 $orders = $this->get_meta( 'order-completed' );
1007
1008 if ( $orders ) {
1009 $count = sizeof( $orders );
1010 } else {
1011 $count = 0;
1012 }
1013
1014 return $count;
1015 }*/
1016
1017 /**
1018 * Check if course contain an item in curriculum.
1019 * Actually, find the item in each section inside curriculum.
1020 *
1021 * @param $item_id
1022 *
1023 * @return bool
1024 */
1025 public function has_item( $item_id ) {
1026 $found = false;
1027 $items = $this->get_item_ids();
1028
1029 if ( $items ) {
1030 $found = in_array( $item_id, $items );
1031 }
1032
1033 return apply_filters( 'learn-press/course-has-item', $found, $item_id, $this->get_id() );
1034 }
1035
1036 /**
1037 * Get course's item (lesson/quiz/etc...).
1038 *
1039 * @param int $item_id Course's item Id.
1040 *
1041 * @return LP_Lesson|LP_Quiz|boolean
1042 */
1043 public function get_item( $item_id ) {
1044 $item = LP_Course_Item::get_item( $item_id );
1045 if ( $item instanceof LP_Course_Item ) {
1046 $item->set_course( $this->get_id() );
1047 }
1048
1049 return apply_filters( 'learn-press/course-item', $item, $item_id, $this->get_id() );
1050 }
1051
1052 /**
1053 * Get course passing condition value.
1054 *
1055 * @param bool $format
1056 * @param string $context
1057 *
1058 * @return array|mixed|string
1059 */
1060 public function get_passing_condition( $format = false ) {
1061 $value = absint( $this->get_data( 'passing_condition' ) );
1062
1063 if ( $format ) {
1064 $value = "{$value}%";
1065 }
1066
1067 return apply_filters(
1068 'learn-press/course-passing-condition',
1069 $value,
1070 $format,
1071 $this->get_id()
1072 );
1073 }
1074
1075 /**
1076 * Get item's link
1077 *
1078 * @param int $item_id
1079 *
1080 * @editor tungnx
1081 * @since 3.0.0
1082 * @version 1.0.1
1083 * @return string
1084 */
1085 public function get_item_link( int $item_id ): string {
1086 $item_link = '';
1087 $item_type = get_post_type( $item_id );
1088
1089 $course_permalink = trailingslashit( $this->get_permalink() );
1090 $item_slug = get_post_field( 'post_name', $item_id );
1091
1092 $slug_prefixes = apply_filters(
1093 'learn-press/course/custom-item-prefixes',
1094 array(
1095 LP_QUIZ_CPT => sanitize_title_with_dashes( LP_Settings::get_option( 'quiz_slug', 'quizzes' ) ),
1096 LP_LESSON_CPT => sanitize_title_with_dashes( LP_Settings::get_option( 'lesson_slug', 'lessons' ) ),
1097 ),
1098 $this->get_id()
1099 );
1100
1101 $slug_prefix = trailingslashit( $slug_prefixes[ $item_type ] ?? '' );
1102 $item_link = trailingslashit( $course_permalink . $slug_prefix . $item_slug );
1103
1104 return apply_filters( 'learn-press/course/item-link', $item_link, $item_id, $this );
1105 }
1106
1107 /**
1108 * Get course's item at a position.
1109 *
1110 * @param int $at
1111 *
1112 * @return bool|mixed
1113 * @editor tungnx
1114 * @modify 4.1.3 - comment - not use
1115 */
1116 /*public function get_item_at( $at ) {
1117 $items = $this->get_items();
1118
1119 if ( ! $items ) {
1120 return false;
1121 }
1122
1123 return ! empty( $items[ $at ] ) ? $items[ $at ] : false;
1124 }*/
1125
1126 /**
1127 * Get position of an item in course curriculum.
1128 *
1129 * @param LP_Course_Item|LP_User_Item|int $item
1130 *
1131 * @return mixed
1132 * @deprecated 4.1.6.9
1133 */
1134 /*public function get_item_position( $item ) {
1135 $items = $this->get_items();
1136
1137 if ( ! $items ) {
1138 return false;
1139 }
1140
1141 $item_id = is_a( $item, 'LP_User_Item' ) || is_a(
1142 $item,
1143 'LP_Course_Item'
1144 ) ? $item->get_id() : absint( $item );
1145
1146 return array_search( $item_id, $items );
1147 }*/
1148
1149 /**
1150 * @return bool|mixed
1151 */
1152 public function get_current_item() {
1153 return $this->is_viewing_item();
1154 }
1155
1156 /**
1157 * Get item standing after the item is viewing.
1158 *
1159 * @param array $args
1160 *
1161 * @return int
1162 */
1163 public function get_next_item( $args = null ) {
1164 $item_nav = $this->get_item_nav();
1165
1166 return apply_filters( 'learn-press/course/next-item', $item_nav ? $item_nav[2] : 0, $this->get_id(), $args );
1167 }
1168
1169 /**
1170 * Get item standing before the item is viewing.
1171 *
1172 * @param array $args
1173 *
1174 * @return int
1175 */
1176 public function get_prev_item( $args = null ) {
1177 $item_nav = $this->get_item_nav();
1178 if ( ! is_array( $item_nav ) || empty( $item_nav ) ) {
1179 return 0;
1180 }
1181
1182 return apply_filters( 'learn-press/course/prev-item', $item_nav[0], $this->get_id(), $args );
1183 }
1184
1185 /**
1186 * Get item standing before and after an item.
1187 * If the item is not passed consider it is item viewing.
1188 *
1189 * @param bool $current_item
1190 * @param bool $viewable - Optional. TRUE will get next item is viewable.
1191 *
1192 * @return array|bool
1193 * @since 3.1.0
1194 */
1195 public function get_item_nav( $current_item = false, $viewable = false ) {
1196 if ( false === $current_item ) {
1197 $current_item = $this->get_current_item();
1198 }
1199
1200 if ( false === $current_item ) {
1201 return false;
1202 }
1203
1204 $prev_id = $next_id = 0;
1205 $item_ids = $this->get_item_ids();
1206
1207 if ( $item_ids ) {
1208 $pos = array_search( $current_item, $item_ids );
1209
1210 if ( false !== $pos ) {
1211 $max = sizeof( $item_ids ) - 1;
1212 $user = learn_press_get_current_user();
1213 $pos_tmp = $pos;
1214
1215 while ( $pos_tmp < $max ) {
1216 ++$pos_tmp;
1217
1218 if ( ! $viewable ) {
1219 $next_id = $item_ids[ $pos_tmp ];
1220
1221 break;
1222 }
1223 }
1224
1225 $pos_tmp = $pos;
1226
1227 while ( $pos_tmp > 0 ) {
1228 --$pos_tmp;
1229
1230 if ( ! $viewable ) {
1231 $prev_id = $item_ids[ $pos_tmp ];
1232
1233 break;
1234 }
1235 }
1236 }
1237 }
1238
1239 return array( $prev_id, $current_item, $next_id );
1240 }
1241
1242 /**
1243 * Get link of item is standing after the item is viewing.
1244 *
1245 * @param array $args
1246 *
1247 * @return string
1248 */
1249 public function get_next_item_html( $args = null ) {
1250 $args = wp_parse_args(
1251 $args,
1252 array(
1253 'current_item' => false,
1254 'viewable' => null,
1255 'dir' => 'next',
1256 )
1257 );
1258
1259 $next_item = $this->get_next_item( $args );
1260
1261 if ( $next_item ) {
1262 ob_start();
1263
1264 learn_press_get_template(
1265 'content-lesson/next-button.php',
1266 array(
1267 'item' => $next_item,
1268 'course' => $this,
1269 )
1270 );
1271
1272 return ob_get_clean();
1273 }
1274
1275 return false;
1276 }
1277
1278 public function get_prev_item_html( $args = null ) {
1279 }
1280
1281 public function get_preview_items() {
1282 return LP_Object_Cache::get( 'course-' . $this->get_id(), 'learn-press/course-preview-items' );
1283 }
1284
1285 /**
1286 * Check a quiz is a final quiz in this course
1287 *
1288 * @param $quiz_id
1289 *
1290 * @return mixed
1291 */
1292 public function is_final_quiz( $quiz_id ) {
1293 return apply_filters( 'learn_press_is_final_quiz', $this->get_final_quiz() == $quiz_id, $quiz_id, $this->get_id() );
1294 }
1295
1296 public function get_final_quiz() {
1297 $final_quiz = $this->get_data( 'final_quiz' );
1298
1299 return apply_filters( 'learn-press/course-final-quiz', $final_quiz, $this->get_id() );
1300 }
1301
1302 public function set_final_quiz( $id ) {
1303 $this->_set_data( 'final_quiz', $id );
1304 }
1305
1306 /**
1307 * Get course duration in seconds
1308 *
1309 * @return int
1310 */
1311 public function get_duration() {
1312 /**
1313 * Duration is in string such as 10 week, 4 hour, etc...
1314 * So we can use strtotime('+10 week') to convert it to seconds
1315 */
1316 return strtotime( '+' . $this->get_data( 'duration' ), 0 );
1317 }
1318
1319
1320 /**
1321 * Get course remaining time message
1322 *
1323 * @param $user_id
1324 *
1325 * @return string
1326 */
1327 public function get_user_duration_html( $user_id = 0 ) {
1328 if ( ! $user_id ) {
1329 $user_id = get_current_user_id();
1330 }
1331
1332 $duration = $this->get_duration();
1333 $user = learn_press_get_user( $user_id );
1334 $course_info = $user->get_course_info( $this->get_id() );
1335 $html = '';
1336
1337 if ( $course_info ) {
1338 $now = current_time( 'timestamp' );
1339 $start_time = intval( strtotime( $course_info['start'] ) );
1340
1341 if ( $start_time + $duration > $now ) {
1342 $remain = $start_time + $duration - $now;
1343 $remain = learn_press_seconds_to_weeks( $remain );
1344 $html = sprintf( __( 'This course will end within the next %s', 'learnpress' ), $remain );
1345 }
1346 }
1347
1348 return $html;
1349 }
1350
1351 /**
1352 * Output params for single course page
1353 *
1354 * @param null $args
1355 *
1356 * @return mixed
1357 */
1358 public function output_args( $args = null ) {
1359 return array();
1360 }
1361
1362 /**
1363 * Get external link of "Buy this course" button
1364 *
1365 * @return mixed
1366 */
1367 public function get_external_link() {
1368 return apply_filters( 'learn-press/course-external-link', $this->get_data( 'external_link', '' ), $this->get_id() );
1369 }
1370
1371 public function get_external_link_text() {
1372 return apply_filters( 'learn-press/course-external-link-text', _x( 'More Info', 'External Link button text', 'learnpress' ), $this->get_id() );
1373 }
1374
1375 /**
1376 * Get main author of course.
1377 *
1378 * @param string $field
1379 *
1380 * @return LP_User|int
1381 */
1382 public function get_author( string $field = '' ) {
1383 $author_id = absint( get_post_field( 'post_author', $this->get_id() ) );
1384
1385 return strtolower( $field ) === 'id' ? $author_id : learn_press_get_user( $author_id );
1386 }
1387
1388 /**
1389 * Get author's display name
1390 *
1391 * @return string
1392 * @since 3.0.9
1393 */
1394 public function get_author_display_name() {
1395 $display_name = '';
1396 $user = $this->get_author();
1397
1398 if ( $user ) {
1399 $display_name = $user->get_display_name();
1400 }
1401
1402 return $display_name;
1403 }
1404
1405 /**
1406 * @return mixed
1407 */
1408 public function get_tags() {
1409 return apply_filters( 'learn-press/course-tags', get_the_term_list( $this->get_id(), 'course_tag', __( 'Tags: ', 'learnpress' ), ' ', '' ) );
1410 }
1411
1412 /**
1413 * Get extra info of course.
1414 * Target Audience, Key Features, Requirements, etc...
1415 *
1416 * @param string $type [target_audience, key_features, requirements]
1417 *
1418 * @return string|array
1419 * @since 3.x.x
1420 */
1421 public function get_extra_info( $type ) {
1422 $extra_info_meta = get_post_meta( $this->get_id(), '_lp_' . $type, true );
1423
1424 return apply_filters( 'learn-press/course-extra-info', $extra_info_meta, $type, $this->get_id() );
1425 }
1426
1427 /**
1428 * Get FAQs in Tab metabox.
1429 *
1430 * @return array
1431 * @since 4.0.0
1432 */
1433 public function get_faqs(): array {
1434 $faqs = array();
1435 $data = get_post_meta( $this->get_id(), '_lp_faqs', true );
1436
1437 if ( $data ) {
1438 foreach ( $data as $faq ) {
1439 $faqs[] = array(
1440 'question' => $faq[0],
1441 'answer' => $faq[1],
1442 );
1443 }
1444 }
1445
1446 return apply_filters( 'learn-press/course-faqs', $faqs, $this->get_id() );
1447 }
1448
1449 /**
1450 * Get course is set featured
1451 *
1452 * @return bool
1453 */
1454 public function is_featured(): bool {
1455 return apply_filters(
1456 'learn-press/course-is-featured',
1457 get_post_meta( $this->get_id(), '_lp_featured', true ) === 'yes',
1458 $this->get_id(),
1459 $this
1460 );
1461 }
1462
1463 /**
1464 * [get_downloadable_material get all material files of this course and lesson of this course]
1465 * @return [array] [array of material files or empty array]
1466 * @deprecated 4.2.6.4
1467 */
1468 /*public function get_downloadable_material(): array {
1469 $material = LP_Material_Files_DB::getInstance();
1470 $materials = $material->get_material_by_item_id( $this->get_id(), 1 );
1471 return apply_filters( 'learn-press/course-materials', $materials, $this->get_id() );
1472 }*/
1473 }
1474 }
1475