PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.2.1
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.2.1
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.2.1, at inc/course/abstract-course.php

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