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

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