PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.1.7.2
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.1.7.2
4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 All 139 releases
learnpress / inc / course / abstract-course.php

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

1,377 lines 33.9 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 * @depecated 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 if this course is required enroll or not.
336 *
337 * @param mixed
338 *
339 * @return bool
340 * @depecated 4.0.0
341 */
342 public function is_required_enroll() {
343 //_deprecated_function( __FUNCTION__, '4.1.7.2', 'is_no_required_enroll' );
344 return ! $this->is_no_required_enroll();
345 }
346 /**
347 * Check if this course is required enroll or not.
348 *
349 * @author hungkv
350 * @since 4.0.5
351 * @return bool
352 * @version 1.0.0
353 */
354 public function is_no_required_enroll(): bool {
355 $return = false;
356 if ( $this->get_data( 'no_required_enroll', 'no' ) == 'yes' && ! is_user_logged_in() ) {
357 $return = true;
358 }
359 return apply_filters( 'learn-press/course/require-enrollment', $return, $this->get_id() );
360 }
361
362 /**
363 * @return mixed
364 * @deprecated
365 */
366 public function is_require_enrollment() {
367 return ! $this->is_no_required_enroll();
368 }
369
370 /**
371 * @depecated 4.1.6.9
372 */
373 /*public function get_item_types( $group = false ) {
374 $cache_key = $group ? 'course-item-group-types' : 'course-item-types';
375 $items = LP_Object_Cache::get( 'course-' . $this->get_id(), "learn-press/{$cache_key}" );
376 $items = false;
377
378 if ( false === $items ) {
379 $item_types = array();
380 $items = array();
381 $sections = array();
382 $all_items = $this->_curd->read_course_items( $this->get_id() );
383
384 if ( $all_items ) {
385 foreach ( $all_items as $item ) {
386 if ( empty( $item_types[ $item->type ] ) ) {
387 $item_types[ $item->type ] = array();
388 }
389 $item_types[ $item->type ][] = $item->id;
390 $items[ $item->id ] = $item->type;
391
392 if ( empty( $sections[ $item->section_id ] ) ) {
393 $sections[ $item->section_id ] = array();
394 }
395 $sections[ $item->section_id ][] = $item->id;
396 }
397 }
398
399 LP_Object_Cache::set( 'course-' . $this->get_id(), $item_types, 'learn-press/course-item-group-types' );
400 LP_Object_Cache::set( 'course-' . $this->get_id(), $items, 'learn-press/course-item-types' );
401
402 foreach ( $sections as $section_id => $section_items ) {
403 LP_Object_Cache::set( 'section-' . $section_id, $section_items, 'learn-press/section-items' );
404 }
405
406 $items = $group ? $item_types : $items;
407 }
408
409 return apply_filters( "learn-press/{$cache_key}", $items, $this->get_id() );
410 }*/
411
412 /**
413 * Get all item's ids in a course.
414 *
415 * @param int $section_id
416 *
417 * @return array
418 * @version 4.0.1
419 * @modify 4.1.6.9 tungnx
420 */
421 public function get_item_ids( int $section_id = 0 ): array {
422 $item_ids = array();
423
424 $sections_items = $this->get_full_sections_and_items_course();
425 foreach ( $sections_items as $section_items ) {
426 foreach ( $section_items->items as $item ) {
427 if ( $section_id ) {
428 if ( $section_id == $section_items->id ) {
429 $item_ids[] = $item->id;
430 }
431
432 continue;
433 }
434
435 $item_ids[] = $item->id;
436 }
437 }
438
439 return apply_filters( 'learn-press/course-item-ids', $item_ids, $this->get_id() );
440 }
441
442 /**
443 * Get item is viewing in single course.
444 *
445 * @return LP_Course_Item
446 * @depecated 4.1.6.9
447 */
448 public function get_viewing_item() {
449 _deprecated_function( __FUNCTION__, '4.1.6.9' );
450 //return apply_filters( 'learn-press/single-course-viewing-item', $this->_viewing_item, $this->get_id() );
451 }
452
453 /**
454 * Get total students fake.
455 *
456 * @return int
457 */
458 public function get_fake_students() : int {
459 return absint( $this->get_data( 'fake_students', 0 ) );
460 }
461
462 /**
463 * Count the real users has enrolled
464 *
465 * @return int
466 */
467 public function get_users_enrolled() {
468 $enrolled = $this->get_data( 'students' );
469
470 if ( false === $enrolled ) {
471 $enrolled = $this->count_students();
472
473 $this->_set_data( 'students', $enrolled );
474 }
475
476 $enrolled = absint( $enrolled );
477
478 return apply_filters( 'learn-press/course/users-enrolled', $enrolled, $this );
479 }
480
481 /**
482 * @param string $field
483 *
484 * @return LP_User|mixed
485 */
486 public function get_instructor( $field = '' ) {
487 $user = learn_press_get_user( get_post_field( 'post_author', $this->get_id() ) );
488
489 return $field ? $user->get_data( $field ) : $user;
490 }
491
492 /**
493 * @return mixed
494 */
495 public function get_instructor_name() {
496 $instructor = $this->get_instructor();
497 $name = '';
498
499 if ( $instructor ) {
500 if ( $instructor->get_data( 'display_name' ) ) {
501 $name = $instructor->get_data( 'display_name' );
502 } elseif ( $instructor->get_data( 'user_nicename' ) ) {
503 $name = $instructor->get_data( 'user_nicename' );
504 } elseif ( $instructor->get_data( 'user_login' ) ) {
505 $name = $instructor->get_data( 'user_login' );
506 }
507 }
508
509 return apply_filters( 'learn-press/course/instructor-name', $name, $this->get_id() );
510 }
511
512 /**
513 * @param int|bool $with_avatar
514 * @param string $link_class
515 *
516 * @return string
517 */
518 public function get_instructor_html( $with_avatar = false, $link_class = '' ) {
519 $instructor = $this->get_instructor_name();
520
521 $html = sprintf(
522 '<a href="%s"%s>%s<span>%s</span></a>',
523 learn_press_user_profile_link( get_post_field( 'post_author', $this->get_id() ) ),
524 $link_class ? sprintf( 'class="%s"', $link_class ) : '',
525 $with_avatar ? get_avatar(
526 $this->get_instructor( 'id' ),
527 $with_avatar === true ? 48 : $with_avatar
528 ) : '',
529 $instructor
530 );
531
532 return apply_filters(
533 'learn_press_course_instructor_html',
534 $html,
535 get_post_field( 'post_author', $this->get_id() ),
536 $this->get_id()
537 );
538 }
539
540 /**
541 * Check if a course is Free
542 *
543 * @return bool
544 */
545 public function is_free(): bool {
546 return apply_filters( 'learn-press/course/is-free', $this->get_price() == 0, $this->get_id() );
547 }
548
549 /**
550 * Get the sale price of course. Check if sale price is set
551 * and the dates are valid.
552 *
553 * @return string|float
554 */
555 public function get_sale_price() {
556 $sale_price_value = $this->get_data( 'sale_price', '' );
557
558 if ( '' !== $sale_price_value ) {
559 return floatval( $sale_price_value );
560 }
561
562 return $sale_price_value;
563 }
564
565 /**
566 * Check course has 'sale price'
567 *
568 * @return mixed
569 */
570 public function has_sale_price() {
571 $has_sale_price = false;
572 $regular_price = $this->get_regular_price();
573 $sale_price = $this->get_sale_price();
574 $start_date = $this->get_data( 'sale_start', '' );
575 $end_date = $this->get_data( 'sale_end', '' );
576
577 if ( $regular_price > $sale_price && is_float( $sale_price ) ) {
578 $has_sale_price = true;
579 }
580
581 // Check in days sale
582 if ( $has_sale_price && '' !== $start_date && '' !== $end_date ) {
583 $now = strtotime( get_date_from_gmt( gmdate( 'Y-m-d H:i:s', time() ), 'Y-m-d H:i:s' ) );
584 $end = strtotime( $end_date );
585 $start = strtotime( $start_date );
586
587 $has_sale_price = $now >= $start && $now <= $end;
588 }
589
590 return apply_filters( 'learn-press/course/has-sale-price', $has_sale_price, $this->get_id() );
591 }
592
593 /**
594 * Get the regular price of course.
595 *
596 * @return float
597 */
598 public function get_regular_price(): float {
599 $price = floatval( $this->get_data( 'regular_price', 0 ) );
600
601 return apply_filters( 'learn-press/course/regular-price', $price, $this->get_id() );
602 }
603
604 /**
605 * Get the regular price format of course.
606 *
607 * @since 4.1.5
608 * @version 1.0.0
609 * @author tungnx
610 * @return mixed
611 */
612 public function get_regular_price_html() {
613 $price = learn_press_format_price( $this->get_regular_price(), true );
614
615 return apply_filters( 'learn-press/course/regular-price', $price, $this->get_id() );
616 }
617
618 /**
619 * Get the price of course.
620 *
621 * @return mixed
622 */
623 public function get_price() {
624 $key_cache = "{$this->get_id()}/price";
625 $price = LP_Course_Cache::cache_load_first( 'get', $key_cache );
626
627 if ( false === $price ) {
628 if ( $this->has_sale_price() ) {
629 $price = $this->get_sale_price();
630 // Add key _lp_course_is_sale for query
631 update_post_meta( $this->get_id(), '_lp_course_is_sale', 1 );
632 } else {
633 // Delete key _lp_course_is_sale
634 delete_post_meta( $this->get_id(), '_lp_course_is_sale' );
635 $price = $this->get_regular_price();
636 }
637
638 // For case set sale by days range
639 update_post_meta( $this->get_id(), '_lp_price', $price );
640
641 LP_Course_Cache::cache_load_first( 'set', $key_cache, $price );
642 }
643
644 return apply_filters( 'learn-press/course/price', $price, $this->get_id() );
645 }
646
647 /**
648 * Get html course price
649 *
650 * @author tungnx
651 * @since 4.1.5
652 * @version 1.0.0
653 * @return mixed|void
654 */
655 public function get_course_price_html() {
656 $price_html = '';
657
658 if ( $this->is_free() ) {
659 if ( is_float( $this->get_sale_price() ) ) {
660 $price_html .= sprintf( '<span class="origin-price">%s</span>', $this->get_regular_price_html() );
661 }
662
663 $price_html .= sprintf( '<span class="free">%s</span>', esc_html__( 'Free', 'learnpress' ) );
664 $price_html = apply_filters( 'learn_press_course_price_html_free', $price_html, $this );
665 } else {
666 if ( $this->has_sale_price() ) {
667 $price_html .= sprintf( '<span class="origin-price">%s</span>', $this->get_regular_price_html() );
668 }
669
670 $price_html .= sprintf( '<span class="price">%s</span>', learn_press_format_price( $this->get_price(), true ) );
671 $price_html = apply_filters( 'learn_press_course_price_html', $price_html, $this->has_sale_price(), $this->get_id() );
672 }
673
674 return $price_html;
675 }
676
677 /**
678 * Get the price of course with html
679 *
680 * @editor tungnx
681 * @modify 4.1.5
682 * @version 1.0.1
683 * @return mixed
684 * @deprecated 4.1.5
685 */
686 public function get_price_html() {
687 $price_html = '';
688
689 if ( $this->is_free() ) {
690 $price_html = apply_filters(
691 'learn_press_course_price_html_free',
692 esc_html__( 'Free', 'learnpress' ),
693 $this
694 );
695 } else {
696 $price_html .= learn_press_format_price( $this->get_price() );
697 $price_html = apply_filters( 'learn_press_course_price_html', $price_html, $this->has_sale_price(), $this->get_id() );
698 }
699
700 return $price_html;
701 }
702
703 /**
704 * Get the origin price of course
705 *
706 * @return float
707 * @deprecated 4.1.5
708 */
709 public function get_origin_price() {
710 return $this->get_regular_price();
711 }
712
713 /**
714 * Get the price of course with html
715 *
716 * @return mixed
717 * @deprecated 4.1.5
718 */
719 public function get_origin_price_html() {
720 return $this->get_regular_price_html();
721 }
722
723 /**
724 * @param bool $item_id
725 *
726 * @return bool|mixed
727 */
728 public function is_viewing_item( $item_id = false ) {
729 $item = LP_Global::course_item();
730
731 if ( empty( $item ) ) {
732 return false;
733 }
734
735 return apply_filters(
736 'learn-press/is-viewing-item',
737 false !== $item_id ? $item_id == $item->get_id() : $item->get_id(),
738 $item_id,
739 $this->get_id()
740 );
741 }
742
743 /**
744 * @param $item_id
745 *
746 * @return bool|mixed
747 */
748 public function is_current_item( $item_id ) {
749 return $this->is_viewing_item( $item_id );
750 }
751
752 /**
753 * Check if the course has 'feature'
754 * This function call to a function with prefix 'has'
755 *
756 * @param string
757 *
758 * @return mixed
759 * @throws Exception
760 */
761 public function has( $tag ) {
762 _deprecated_function( __FUNCTION__, '3.0.8' );
763
764 $args = func_get_args();
765 unset( $args[0] );
766 $method = 'has_' . preg_replace( '!-!', '_', $tag );
767 $callback = array( $this, $method );
768
769 if ( is_callable( $callback ) ) {
770 return call_user_func_array( $callback, $args );
771 } else {
772 throw new Exception( sprintf( __( 'The function %s doesn\'t exist', 'learnpress' ), $tag ) );
773 }
774 }
775
776 /**
777 * @param string
778 *
779 * @return mixed
780 * @throws Exception
781 */
782 public function is( $tag ) {
783 _deprecated_function( __FUNCTION__, '3.0.8' );
784 $args = func_get_args();
785 unset( $args[0] );
786 $method = 'is_' . preg_replace( '!-!', '_', $tag );
787 $callback = array( $this, $method );
788
789 if ( is_callable( $callback ) ) {
790 return call_user_func_array( $callback, $args );
791 } else {
792 throw new Exception( sprintf( __( 'The function %s doesn\'t exist', 'learnpress' ), $tag ) );
793 }
794 }
795
796 /**
797 * Return true if this course can be purchasable.
798 * Required enroll.
799 * Status is publish.
800 *
801 * @return mixed
802 */
803 public function is_purchasable() {
804 $is_purchasable = $this->exists() && ! $this->is_no_required_enroll() && get_post_status( $this->get_id() ) == 'publish';
805
806 return apply_filters( 'learn-press/is-purchasable', $is_purchasable, $this->get_id() );
807 }
808
809 /**
810 * Check if students have enrolled or purchased course is reached.
811 *
812 * @return mixed
813 */
814 public function is_in_stock() {
815 $in_stock = true;
816 $max_allowed = $this->get_max_students();
817
818 if ( $max_allowed ) {
819 $in_stock = $max_allowed > $this->get_total_user_enrolled_or_purchased();
820 }
821
822 return apply_filters( 'learn-press/is-in-stock', $in_stock, $this->get_id() );
823 }
824
825 /**
826 * Get max students can enroll to course.
827 *
828 * @return int
829 *
830 * @since 3.0.0
831 */
832 public function get_max_students() {
833 return apply_filters(
834 'learn-press/max-students',
835 absint( $this->get_data( 'max_students' ) ),
836 $this->get_id()
837 );
838 }
839
840 /**
841 * Count number of students enrolled course.
842 * Check global settings `enrolled_students_number`
843 * and add the fake value if both are set.
844 *
845 * @return int
846 * @editor tungnx
847 * @Todo: view and rewrite this function
848 */
849 public function count_students(): int {
850 $key_cache = "{$this->get_id()}/total-students";
851 $total = LP_Course_Cache::instance()->get_cache( $key_cache );
852
853 if ( $total ) {
854 return $total;
855 }
856
857 $lp_course_db = LP_Course_DB::getInstance();
858 $total = $lp_course_db->get_total_user_enrolled( $this->get_id() );
859 $total += $this->get_fake_students();
860
861 LP_Course_Cache::instance()->set_cache( $key_cache, $total, 6 * HOUR_IN_SECONDS );
862
863 return $total;
864 }
865
866 /**
867 * Get total user enrolled
868 *
869 * @return int
870 */
871 public function get_total_user_enrolled(): int {
872 $lp_course_db = LP_Course_DB::getInstance();
873 return $lp_course_db->get_total_user_enrolled( $this->get_id() );
874 }
875
876 /**
877 * Get total user enrolled or finished
878 *
879 * @return int
880 */
881 public function get_total_user_enrolled_or_purchased(): int {
882 $key_cache = "{$this->get_id()}/total-students-attended";
883 $total_user_enrolled = LP_Course_Cache::cache_load_first( 'get', $key_cache );
884
885 if ( false === $total_user_enrolled ) {
886 $lp_course_db = LP_Course_DB::getInstance();
887 $total_user_enrolled = $lp_course_db->get_total_user_enrolled_or_purchased( $this->get_id() );
888
889 LP_Course_Cache::cache_load_first( 'set', $key_cache, $total_user_enrolled );
890 }
891
892 return $total_user_enrolled;
893 }
894
895 /**
896 * @param string|array $statuses
897 *
898 * @return mixed
899 */
900 public function count_in_order( $statuses = 'completed' ) {
901 settype( $statuses, 'array' );
902 $count = 0;
903
904 foreach ( $statuses as $status ) {
905 $orders = get_post_meta( $this->get_id(), 'order-' . $status, true );
906
907 if ( $orders ) {
908 $count += sizeof( $orders );
909 }
910 }
911
912 return $count;
913 }
914
915 /**
916 * @editor tungnx
917 * @modify 4.1.4 - comment - not use
918 */
919 /*public function count_completed_orders() {
920 $orders = $this->get_meta( 'order-completed' );
921
922 if ( $orders ) {
923 $count = sizeof( $orders );
924 } else {
925 $count = 0;
926 }
927
928 return $count;
929 }*/
930
931 /**
932 * Check if course contain an item in curriculum.
933 * Actually, find the item in each section inside curriculum.
934 *
935 * @param $item_id
936 *
937 * @return bool
938 */
939 public function has_item( $item_id ) {
940 $found = false;
941 $items = $this->get_item_ids();
942
943 if ( $items ) {
944 $found = in_array( $item_id, $items );
945 }
946
947 return apply_filters( 'learn-press/course-has-item', $found, $item_id, $this->get_id() );
948 }
949
950 /**
951 * Get course's item (lesson/quiz/etc...).
952 *
953 * @param int $item_id Course's item Id.
954 *
955 * @return LP_Lesson|LP_Quiz|boolean
956 */
957 public function get_item( $item_id ) {
958 $item = LP_Course_Item::get_item( $item_id );
959 if ( $item instanceof LP_Course_Item ) {
960 $item->set_course( $this->get_id() );
961 }
962
963 return apply_filters( 'learn-press/course-item', $item, $item_id, $this->get_id() );
964 }
965
966 /**
967 * Get course passing condition value.
968 *
969 * @param bool $format
970 * @param string $context
971 *
972 * @return array|mixed|string
973 */
974 public function get_passing_condition( $format = false, $context = '' ) {
975 $value = absint( $this->get_data( 'passing_condition' ) );
976
977 if ( $format ) {
978 $value = "{$value}%";
979 }
980
981 return 'edit' === $context ? $value : apply_filters(
982 'learn-press/course-passing-condition',
983 $value,
984 $format,
985 $this->get_id()
986 );
987 }
988
989 /**
990 * Get item's link
991 *
992 * @param int $item_id
993 *
994 * @editor tungnx
995 * @since 3.0.0
996 * @version 1.0.1
997 * @return string
998 */
999 public function get_item_link( int $item_id ): string {
1000 $item_link = '';
1001 $item_type = get_post_type( $item_id );
1002
1003 $course_permalink = trailingslashit( $this->get_permalink() );
1004 $item_slug = get_post_field( 'post_name', $item_id );
1005
1006 $slug_prefixes = apply_filters(
1007 'learn-press/course/custom-item-prefixes',
1008 array(
1009 LP_QUIZ_CPT => sanitize_title_with_dashes( LP_Settings::instance()->get( 'quiz_slug', 'quizzes' ) ),
1010 LP_LESSON_CPT => sanitize_title_with_dashes( LP_Settings::instance()->get( 'lesson_slug', 'lessons' ) ),
1011 ),
1012 $this->get_id()
1013 );
1014
1015 $slug_prefix = trailingslashit( $slug_prefixes[ $item_type ] ?? '' );
1016
1017 $item_link = $course_permalink . $slug_prefix . $item_slug;
1018
1019 return apply_filters( 'learn-press/course/item-link', $item_link, $item_id, $this );
1020 }
1021
1022 /**
1023 * Get course's item at a position.
1024 *
1025 * @param int $at
1026 *
1027 * @return bool|mixed
1028 * @editor tungnx
1029 * @modify 4.1.3 - comment - not use
1030 */
1031 /*public function get_item_at( $at ) {
1032 $items = $this->get_items();
1033
1034 if ( ! $items ) {
1035 return false;
1036 }
1037
1038 return ! empty( $items[ $at ] ) ? $items[ $at ] : false;
1039 }*/
1040
1041 /**
1042 * Get position of an item in course curriculum.
1043 *
1044 * @param LP_Course_Item|LP_User_Item|int $item
1045 *
1046 * @return mixed
1047 * @depecated 4.1.6.9
1048 */
1049 /*public function get_item_position( $item ) {
1050 $items = $this->get_items();
1051
1052 if ( ! $items ) {
1053 return false;
1054 }
1055
1056 $item_id = is_a( $item, 'LP_User_Item' ) || is_a(
1057 $item,
1058 'LP_Course_Item'
1059 ) ? $item->get_id() : absint( $item );
1060
1061 return array_search( $item_id, $items );
1062 }*/
1063
1064 /**
1065 * @return bool|mixed
1066 */
1067 public function get_current_item() {
1068 return $this->is_viewing_item();
1069 }
1070
1071 /**
1072 * Get item standing after the item is viewing.
1073 *
1074 * @param array $args
1075 *
1076 * @return int
1077 */
1078 public function get_next_item( $args = null ) {
1079 $item_nav = $this->get_item_nav();
1080
1081 return apply_filters( 'learn-press/course/next-item', $item_nav[2], $this->get_id(), $args );
1082 }
1083
1084 /**
1085 * Get item standing before the item is viewing.
1086 *
1087 * @param array $args
1088 *
1089 * @return int
1090 */
1091 public function get_prev_item( $args = null ) {
1092 $item_nav = $this->get_item_nav();
1093
1094 return apply_filters( 'learn-press/course/prev-item', $item_nav[0], $this->get_id(), $args );
1095 }
1096
1097 /**
1098 * Get item standing before and after an item.
1099 * If the item is not passed consider it is item viewing.
1100 *
1101 * @param bool $current_item
1102 * @param bool $viewable - Optional. TRUE will get next item is viewable.
1103 *
1104 * @return array|bool
1105 * @since 3.1.0
1106 */
1107 public function get_item_nav( $current_item = false, $viewable = false ) {
1108 if ( false === $current_item ) {
1109 $current_item = $this->get_current_item();
1110 }
1111
1112 if ( false === $current_item ) {
1113 return false;
1114 }
1115
1116 $prev_id = $next_id = 0;
1117 $item_ids = $this->get_item_ids();
1118
1119 if ( $item_ids ) {
1120 $pos = array_search( $current_item, $item_ids );
1121
1122 if ( false !== $pos ) {
1123 $max = sizeof( $item_ids ) - 1;
1124 $user = learn_press_get_current_user();
1125 $pos_tmp = $pos;
1126
1127 while ( $pos_tmp < $max ) {
1128 $pos_tmp ++;
1129
1130 if ( ! $viewable ) {
1131 $next_id = $item_ids[ $pos_tmp ];
1132
1133 break;
1134 }
1135 }
1136
1137 $pos_tmp = $pos;
1138
1139 while ( $pos_tmp > 0 ) {
1140 $pos_tmp --;
1141
1142 if ( ! $viewable ) {
1143 $prev_id = $item_ids[ $pos_tmp ];
1144
1145 break;
1146 }
1147 }
1148 }
1149 }
1150
1151 return array( $prev_id, $current_item, $next_id );
1152 }
1153
1154 /**
1155 * Get link of item is standing after the item is viewing.
1156 *
1157 * @param array $args
1158 *
1159 * @return string
1160 */
1161 public function get_next_item_html( $args = null ) {
1162 $args = wp_parse_args(
1163 $args,
1164 array(
1165 'current_item' => false,
1166 'viewable' => null,
1167 'dir' => 'next',
1168 )
1169 );
1170
1171 $next_item = $this->get_next_item( $args );
1172
1173 if ( $next_item ) {
1174 ob_start();
1175
1176 learn_press_get_template(
1177 'content-lesson/next-button.php',
1178 array(
1179 'item' => $next_item,
1180 'course' => $this,
1181 )
1182 );
1183
1184 return ob_get_clean();
1185 }
1186
1187 return false;
1188 }
1189
1190 public function get_prev_item_html( $args = null ) {
1191
1192 }
1193
1194 public function get_preview_items() {
1195 return LP_Object_Cache::get( 'course-' . $this->get_id(), 'learn-press/course-preview-items' );
1196 }
1197
1198 /**
1199 * Check a quiz is a final quiz in this course
1200 *
1201 * @param $quiz_id
1202 *
1203 * @return mixed
1204 */
1205 public function is_final_quiz( $quiz_id ) {
1206 return apply_filters( 'learn_press_is_final_quiz', $this->get_final_quiz() == $quiz_id, $quiz_id, $this->get_id() );
1207 }
1208
1209 public function get_final_quiz() {
1210 $final_quiz = $this->get_data( 'final_quiz' );
1211
1212 return apply_filters( 'learn-press/course-final-quiz', $final_quiz, $this->get_id() );
1213 }
1214
1215 public function set_final_quiz( $id ) {
1216 $this->_set_data( 'final_quiz', $id );
1217 }
1218
1219 /**
1220 * Get course duration in seconds
1221 *
1222 * @return int
1223 */
1224 public function get_duration() {
1225 /**
1226 * Duration is in string such as 10 week, 4 hour, etc...
1227 * So we can use strtotime('+10 week') to convert it to seconds
1228 */
1229 return strtotime( '+' . $this->get_data( 'duration' ), 0 );
1230 }
1231
1232
1233 /**
1234 * Get course remaining time message
1235 *
1236 * @param $user_id
1237 *
1238 * @return string
1239 */
1240 public function get_user_duration_html( $user_id = 0 ) {
1241 if ( ! $user_id ) {
1242 $user_id = get_current_user_id();
1243 }
1244
1245 $duration = $this->get_duration();
1246 $user = learn_press_get_user( $user_id );
1247 $course_info = $user->get_course_info( $this->get_id() );
1248 $html = '';
1249
1250 if ( $course_info ) {
1251 $now = current_time( 'timestamp' );
1252 $start_time = intval( strtotime( $course_info['start'] ) );
1253
1254 if ( $start_time + $duration > $now ) {
1255 $remain = $start_time + $duration - $now;
1256 $remain = learn_press_seconds_to_weeks( $remain );
1257 $html = sprintf( __( 'This course will end within next %s', 'learnpress' ), $remain );
1258 }
1259 }
1260
1261 return $html;
1262 }
1263
1264 /**
1265 * Output params for single course page
1266 *
1267 * @param null $args
1268 *
1269 * @return mixed
1270 */
1271 public function output_args( $args = null ) {
1272 return array();
1273 }
1274
1275 /**
1276 * Get external link of "Buy this course" button
1277 *
1278 * @return mixed
1279 */
1280 public function get_external_link() {
1281 return apply_filters( 'learn-press/course-external-link', $this->get_data( 'external_link', '' ), $this->get_id() );
1282 }
1283
1284 public function get_external_link_text() {
1285 return apply_filters( 'learn-press/course-external-link-text', _x( 'More Info', 'External Link button text', 'learnpress' ), $this->get_id() );
1286 }
1287
1288 /**
1289 * Get main author of course.
1290 *
1291 * @param string $field
1292 *
1293 * @return LP_User|int
1294 */
1295 public function get_author( string $field = '' ) {
1296 $author_id = absint( get_post_field( 'post_author', $this->get_id() ) );
1297
1298 return strtolower( $field ) === 'id' ? $author_id : learn_press_get_user( $author_id );
1299 }
1300
1301 /**
1302 * Get author's display name
1303 *
1304 * @return string
1305 * @since 3.0.9
1306 */
1307 public function get_author_display_name() {
1308 $display_name = '';
1309 $user = $this->get_author();
1310
1311 if ( $user ) {
1312 $display_name = $user->get_display_name();
1313 }
1314
1315 return $display_name;
1316 }
1317
1318 /**
1319 * @return mixed
1320 */
1321 public function get_tags() {
1322 return apply_filters( 'learn-press/course-tags', get_the_term_list( $this->get_id(), 'course_tag', __( 'Tags: ', 'learnpress' ), ' ', '' ) );
1323 }
1324
1325 /**
1326 * Get extra info of course.
1327 * Target Audience, Key Features, Requirements, etc...
1328 *
1329 * @param string $type [target_audience, key_features, requirements]
1330 *
1331 * @return string|array
1332 * @since 3.x.x
1333 */
1334 public function get_extra_info( $type ) {
1335 $extra_info_meta = get_post_meta( $this->get_id(), '_lp_' . $type, true );
1336
1337 return apply_filters( 'learn-press/course-extra-info', $extra_info_meta, $type, $this->get_id() );
1338 }
1339
1340 /**
1341 * Get FAQs in Tab metabox.
1342 *
1343 * @return array
1344 * @since 4.0.0
1345 */
1346 public function get_faqs(): array {
1347 $faqs = array();
1348 $data = get_post_meta( $this->get_id(), '_lp_faqs', true );
1349
1350 if ( $data ) {
1351 foreach ( $data as $faq ) {
1352 $faqs[] = array(
1353 'question' => $faq[0],
1354 'answer' => $faq[1],
1355 );
1356 }
1357 }
1358
1359 return apply_filters( 'learn-press/course-faqs', $faqs, $this->get_id() );
1360 }
1361
1362 /**
1363 * Get course is set featured
1364 *
1365 * @return bool
1366 */
1367 public function is_featured(): bool {
1368 return apply_filters(
1369 'learn-press/course-is-featured',
1370 get_post_meta( $this->get_id(), '_lp_featured', true ) === 'yes',
1371 $this->get_id(),
1372 $this
1373 );
1374 }
1375 }
1376 }
1377